TypeScript requires explicit typing for the children prop in React components. Using React.PropsWithChildren ensures the children prop is correctly typed as ReactNode and helps prevent type errors when rendering components with child elements.
Fixes Props interface must extend React.PropsWithChildren for correct typing
import React from 'react';
interface ButtonProps {
backgroundColor: string;
onClick: () => void;
}
// Wrap your props with PropsWithChildren
const Button = ({ backgroundColor, onClick, children }: React.PropsWithChildren<ButtonProps>) => {
return (
<button style={{ backgroundColor }} onClick={onClick}>
{children}
</button>
);
};import React from 'react';interface ButtonProps {backgroundColor: string;onClick: () => void;}// Wrap your props with PropsWithChildrenconst Button = ({ backgroundColor, onClick, children }: React.PropsWithChildren<ButtonProps>) => {return (<button style={{ backgroundColor }} onClick={onClick}>{children}</button>);};Props interface must extend React.PropsWithChildren for correct typing
This TypeScript error occurs when you try to use the `children` prop in a React component without properly typing it in your props interface. Since React 18, the `children` prop is no longer implicitly included in React.FC, requiring developers to explicitly type it. The `React.PropsWithChildren` utility type is React's built-in solution for adding the children prop to your component's props. It's defined as `type PropsWithChildren<P> = P & { children?: ReactNode }`, which merges your custom props with an optional children property typed as ReactNode. Without proper typing, TypeScript cannot verify that your component is being used correctly, leading to potential runtime errors when components expect children but receive none, or when the wrong type of children is passed.
The most straightforward approach is to wrap your props interface with React.PropsWithChildren:
import React from 'react';
interface ButtonProps {
backgroundColor: string;
onClick: () => void;
}
// Wrap your props with PropsWithChildren
const Button = ({ backgroundColor, onClick, children }: React.PropsWithChildren<ButtonProps>) => {
return (
<button style={{ backgroundColor }} onClick={onClick}>
{children}
</button>
);
};This automatically adds children?: ReactNode to your props type.
For better reusability, have your interface extend React.PropsWithChildren:
import React from 'react';
interface CardProps extends React.PropsWithChildren {
title: string;
variant?: 'primary' | 'secondary';
}
const Card = ({ title, variant = 'primary', children }: CardProps) => {
return (
<div className={`card card-${variant}`}>
<h2>{title}</h2>
<div className="card-content">{children}</div>
</div>
);
};This approach is cleaner when you have multiple props and allows extending multiple interfaces.
If you need more control over whether children is required or optional, manually add it to your props:
import React, { ReactNode } from 'react';
// Optional children
interface ContainerProps {
className?: string;
children?: ReactNode;
}
// Required children
interface WrapperProps {
id: string;
children: ReactNode; // No question mark makes it required
}
const Container = ({ className, children }: ContainerProps) => {
return <div className={className}>{children}</div>;
};
const Wrapper = ({ id, children }: WrapperProps) => {
return <section id={id}>{children}</section>;
};Note that PropsWithChildren makes children optional by default. Use explicit typing if children should be required.
If you're using React.FC, you must now explicitly include children:
import React from 'react';
// Before React 18 (children was implicit)
// const Layout: React.FC<{ title: string }> = ({ title, children }) => { ... }
// After React 18 (children must be explicit)
interface LayoutProps {
title: string;
children?: React.ReactNode;
}
const Layout: React.FC<LayoutProps> = ({ title, children }) => {
return (
<div>
<h1>{title}</h1>
<main>{children}</main>
</div>
);
};
// Or use PropsWithChildren
const Layout: React.FC<React.PropsWithChildren<{ title: string }>> = ({ title, children }) => {
return (
<div>
<h1>{title}</h1>
<main>{children}</main>
</div>
);
};Many developers now prefer to avoid React.FC altogether and use regular function declarations.
PropsWithChildren vs Manual ReactNode
The PropsWithChildren utility type always makes children optional (children?: ReactNode). If your component requires children, you should manually type it as children: ReactNode (without the question mark).
Why React 18 Changed This
Prior to React 18, React.FC implicitly included children, which caused issues:
- Components that shouldn't accept children were typed as accepting them
- It violated the principle of explicit is better than implicit
- It made TypeScript types less accurate
Custom PropsWithChildren Variants
You can create your own utility types for more control:
// Required children
type PropsWithRequiredChildren<P = unknown> = P & { children: ReactNode };
// Specific children type
type PropsWithStringChildren<P = unknown> = P & { children: string };Type Safety Limitations
While TypeScript can enforce that children exists, it cannot deeply validate the structure of JSX children at compile time. You cannot make children "type safe" in the sense of enforcing specific component types or structures through TypeScript alone.