The children prop requires special attention in TypeScript.
React.ReactNode (Most Common)
Accepts anything React can render:
tsxtype CardProps = { title: string; children: React.ReactNode; }; function Card({ title, children }: CardProps) { return ( <div className="card"> <h2>{title}</h2> <div className="card-body">{children}</div> </div> ); } // All valid: <Card title="Hello">Text content</Card> <Card title="Hello"><span>Element</span></Card> <Card title="Hello">{null}</Card> <Card title="Hello">{items.map(i => <Item key={i.id} />)}</Card>
React.ReactElement (Stricter)
Only accepts JSX elements:
tsxtype LayoutProps = { header: React.ReactElement; children: React.ReactElement; }; function Layout({ header, children }: LayoutProps) { return ( <div> <header>{header}</header> <main>{children}</main> </div> ); } // Valid: <Layout header={<Header />}><Content /></Layout> // Error: <Layout header="Just text"><Content /></Layout>
PropsWithChildren Helper
tsximport { PropsWithChildren } from 'react'; type CardProps = PropsWithChildren<{ title: string; }>; // Equivalent to: type CardProps = { title: string; children?: React.ReactNode; };
Specific Child Types
tsx// Only accept specific element types type TabsProps = { children: React.ReactElement<TabProps> | React.ReactElement<TabProps>[]; }; // Function as children (render props) type DataProviderProps = { children: (data: User[]) => React.ReactNode; }; function DataProvider({ children }: DataProviderProps) { const data = useData(); return <>{children(data)}</>; } // Usage: <DataProvider> {(users) => users.map(u => <UserCard key={u.id} user={u} />)} </DataProvider>
No Children
For components that shouldn't have children:
tsxtype IconProps = { name: string; size?: number; // Don't include children }; function Icon({ name, size = 24 }: IconProps) { return <svg>...</svg>; } // TypeScript error if you try: <Icon name="star">Text</Icon>