Typing React Components — Props & Children
React components accept typed props. TypeScript can validate prop shapes, mark props as optional, and precisely type `children`. The `React.FC` type is commonly seen but has a subtle flaw — it's better to write components as plain typed functions and annotate return types explicitly.
Component Props — React.FC vs Explicit Typing
// ── Basic props interface ─────────────────────────────────────
interface TaskCardProps {
task: Task;
onComplete: (id: number) => void;
onDelete?: (id: number) => void; // optional — may not be provided
className?: string;
}
// ── React.FC approach (common but flawed) ─────────────────────
// React.FC implicitly includes children in props (in older versions).
// It also hides the explicit return type.
const TaskCard: React.FC<TaskCardProps> = ({ task, onComplete, onDelete }) => {
return (
<div className="task-card">
<h3>{task.title}</h3>
<button onClick={() => onComplete(task.id)}>Complete</button>
{onDelete && <button onClick={() => onDelete(task.id)}>Delete</button>}
</div>
);
};
// ── Preferred: plain function with explicit return type ────────
function TaskCard({ task, onComplete, onDelete, className = "" }: TaskCardProps): React.ReactElement {
return (
<div className={`task-card ${className}`}>
<h3>{task.title}</h3>
<span className={`badge badge--${task.priority}`}>{task.priority}</span>
<button onClick={() => onComplete(task.id)}>✅ Complete</button>
{onDelete && <button onClick={() => onDelete(task.id)}>🗑️ Delete</button>}
</div>
);
}
// ── Typing children ───────────────────────────────────────────
interface LayoutProps {
children: React.ReactNode; // most permissive: any valid React content
title: string;
}
// React.ReactNode: string | number | ReactElement | ReactFragment | null | boolean
function Layout({ children, title }: LayoutProps): React.ReactElement {
return (
<main>
<header><h1>{title}</h1></header>
<section>{children}</section>
</main>
);
}
// ── Component that renders a single element ────────────────────
interface IconProps {
children: React.ReactElement; // only a single React element
}
// ── Typed render prop ─────────────────────────────────────────
interface TaskListProps<T extends Task> {
tasks: T[];
renderItem: (task: T, index: number) => React.ReactNode;
emptyState?: React.ReactNode;
}
function TaskList<T extends Task>({ tasks, renderItem, emptyState }: TaskListProps<T>): React.ReactElement {
if (tasks.length === 0) {
return <>{emptyState ?? <p>No tasks found.</p>}</>;
}
return <ul>{tasks.map((task, i) => <li key={task.id}>{renderItem(task, i)}</li>)}</ul>;
}Common Mistakes
- Using `React.FC` in modern React — since React 18, `React.FC` no longer implicitly adds `children`. But it still hides the explicit JSX return type. Prefer plain functions with typed params and explicit `: React.ReactElement` return.
- Using `any` for props — every prop should be typed. If you don't know the shape of a prop, use `unknown` and narrow before use, or use a more specific interface.
- Not typing optional props — don't forget `?` for truly optional props, and provide default values in destructuring. Missing required props then become a compile error at the callsite.
Tip
Tip
Practice Typing React Components Props Children in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Props flow down, events flow up — unidirectional data flow
Practice Task
Note
Practice Task — (1) Write a working example of Typing React Components Props Children from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Typing React Components Props Children is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready typescript code.
Key Takeaways
- React components accept typed props.
- Using `React.FC` in modern React — since React 18, `React.FC` no longer implicitly adds `children`. But it still hides the explicit JSX return type. Prefer plain functions with typed params and explicit `: React.ReactElement` return.
- Using `any` for props — every prop should be typed. If you don't know the shape of a prop, use `unknown` and narrow before use, or use a more specific interface.
- Not typing optional props — don't forget `?` for truly optional props, and provide default values in destructuring. Missing required props then become a compile error at the callsite.