The children Prop
Whatever JSX you place between a component’s opening and closing tags is handed to that component as a single special prop called children. This is the foundation of composition in React: instead of components knowing about every piece of content they wrap, they accept arbitrary content and decide where to render it. Mastering children is what lets you build reusable layouts, cards, modals, and wrapper components that stay generic.
What children actually is
When you write <Card>Hello</Card>, React passes the content Hello to Card as props.children. The component reads that prop and renders it wherever it wants. Nothing is rendered automatically — if a component never references children, the nested content disappears.
function Card({ children }) {
return <div className="card">{children}</div>;
}
export default function App() {
return (
<Card>
<h2>Profile</h2>
<p>Composition makes components flexible.</p>
</Card>
);
}
Here Card does not know or care what it wraps. It only provides a styled container, and the caller fills it with any markup. This separation is why children is the most common composition tool in React.
The shape of children
The value of children changes depending on what you pass. Knowing its shape helps you avoid surprises when iterating or counting elements.
| What you pass | Type of children |
|---|---|
| Nothing | undefined |
| A single text node | string |
| One JSX element | a React element object |
| Multiple elements/nodes | an array of nodes |
{condition && <X />} | element or false/null |
Because the type varies, never assume children is always an array. To work with it generically, use the helpers in the react package such as React.Children.map and React.Children.count, which normalize all of these shapes.
import { Children } from "react";
function List({ children }) {
return (
<ul>
{Children.map(children, (child) => (
<li>{child}</li>
))}
</ul>
);
}
Layout and wrapper components
The most valuable use of children is building layout shells. A page layout can define structure once — header, sidebar, footer — and slot the unique content in through children.
function PageLayout({ children }) {
return (
<div className="layout">
<header>DevCraftly</header>
<main>{children}</main>
<footer>© 2026</footer>
</div>
);
}
function HomePage() {
return (
<PageLayout>
<h1>Welcome</h1>
<p>This content fills the main slot.</p>
</PageLayout>
);
}
Reaching for
childrento wrap content is almost always cleaner than passing JSX through a regular prop. It reads naturally as nesting and keeps your JSX tree shallow.
Named slots with props
children gives you one slot. When you need several distinct regions — say a card header and a card body — pass additional JSX-typed props alongside children.
function Panel({ title, actions, children }) {
return (
<section className="panel">
<div className="panel-head">
<h3>{title}</h3>
<div>{actions}</div>
</div>
<div className="panel-body">{children}</div>
</section>
);
}
function Dashboard() {
return (
<Panel title="Reports" actions={<button>Export</button>}>
<p>Body content goes through children.</p>
</Panel>
);
}
Render-by-children composition
children does not have to be static JSX — it can be a function. This “render prop as children” pattern lets a component compute a value and hand it back to the caller, who decides how to render it.
import { useState } from "react";
function MousePosition({ children }) {
const [pos, setPos] = useState({ x: 0, y: 0 });
return (
<div
style={{ height: 200 }}
onMouseMove={(e) => setPos({ x: e.clientX, y: e.clientY })}
>
{children(pos)}
</div>
);
}
function Tracker() {
return (
<MousePosition>
{({ x, y }) => (
<p>
Cursor at {x}, {y}
</p>
)}
</MousePosition>
);
}
Output:
Cursor at 142, 88
While custom hooks have replaced many render-prop use cases, function-as-children remains useful when the rendered markup must live inside the provider’s own DOM subtree.
Typing children in TypeScript
In TypeScript, the modern way to type any renderable content is React.ReactNode. It covers elements, strings, numbers, arrays, fragments, null, and undefined.
import type { ReactNode } from "react";
interface CardProps {
children: ReactNode;
}
function Card({ children }: CardProps) {
return <div className="card">{children}</div>;
}
For a function-as-children component, type children as a function returning ReactNode:
import type { ReactNode } from "react";
interface MouseProps {
children: (pos: { x: number; y: number }) => ReactNode;
}
Avoid the older
React.FCtyping, which implicitly added achildrenprop. Modern React requires you to declarechildrenexplicitly, which is more honest about whether a component accepts nested content.
Best Practices
- Always destructure
{ children }from props so the contract of your component is visible at a glance. - Use
childrenfor the primary content slot and named JSX props for secondary regions like headers or actions. - Reach for
React.Children.map/React.Children.countinstead of treatingchildrenas a plain array — its shape is not guaranteed. - Type
childrenasReactNodein TypeScript, and as a function returningReactNodefor render-by-children. - Prefer custom hooks over function-as-children unless the output must render inside the provider’s DOM.
- Do not mutate or deeply inspect children to drive logic; pass explicit props instead for clearer, more predictable components.