In React, children is the prop that holds whatever you write between a component's opening and closing tags. A component that renders {children} somewhere in its output becomes a wrapper: it supplies the frame, and the caller supplies the content.
Both cards share one border, one padding and one heading style, but each holds completely different content. Add a third <Card title="Help"> with a link inside and it gets the same frame for free.
How children reaches the component
JSX turns nested content into a prop. These two lines produce the same element:
<Card title="Profile"><p>Ada</p></Card>
<Card title="Profile" children={<p>Ada</p>} />
So children is an ordinary prop with a special way of being passed. You destructure it like any other (function Card({ children })) or read it as props.children. The page on props covers props in general.
What arrives in children depends on what you wrote between the tags. One child arrives as that child. Several children arrive as an array. Text arrives as a string, and nothing at all arrives as undefined.
The console shows object, array of 2, string and undefined. You rarely need to care: rendering {children} handles all of these, and null, undefined, true and false render nothing. The shape only matters if you try to inspect or change children, which is what the last section warns against.
Layout components
The most common use of children is a layout component: a page shell, a sidebar layout, a centered column. The component owns the structure and spacing, and every page that uses it passes its own content.
function PageLayout({ children }) {
return (
<div className="page">
<Header />
<main className="page-content">{children}</main>
<Footer />
</div>
);
}
function AboutPage() {
return (
<PageLayout>
<h1>About us</h1>
<p>We teach people to code.</p>
</PageLayout>
);
}
PageLayout does not know or care what an about page contains. That separation is the point: change the header once and every page wrapped in the layout picks it up.
Several slots with named props
children is one slot. When a component needs content in more than one place, pass the extra pieces as named props. Any prop can hold JSX, so a modal can take a title, a footer and its body as children.
The modal shell controls the borders, padding and order of the three areas, and the caller fills each one. Other frameworks call these slots; in React they are just props. The <>...</> around the two buttons is a fragment, which groups them without adding an extra element. A real dialog would also render into document.body with a portal so it sits above the page.
Move the <p> into the title prop and the body text appears in the header: the component decides where each prop goes, the caller only decides what goes in it.
Children as a function
Sometimes the wrapper owns some state and the caller should decide how to draw it. Instead of an element, the caller passes a function as children, and the wrapper calls it with the values. This pattern is called a render prop.
One Toggle draws a button and the other a checkbox, from the same logic. Render props were the main way to share stateful logic before hooks. Today a custom hook such as useToggle() usually reads better, but you will still see render props in libraries for lists, forms and animation.
The Children API and cloneElement
React exports a Children object with helpers for walking children: Children.map, Children.forEach, Children.count, Children.toArray and Children.only. Together with cloneElement, which copies an element with new props, they let a parent inspect and change what it was given. The React team lists them as legacy APIs: they still work, but new code should avoid them.
The reason is that they only see the elements written directly between the tags. They cannot see what those elements render.
The list shows four items, but the console says Children.count = 3, because TwoMore is one child no matter how many items it renders. cloneElement also hands the style prop to TwoMore, which ignores it, so the third and fourth items are not striped. Anyone who refactors a few <li> elements into a component breaks the parent without touching it.
The fix is to stop reaching into children and pass data instead:
function NumberedList({ items }) {
return (
<ol>
{items.map((item, i) => (
<li key={item.id} style={{ color: i % 2 ? 'gray' : 'black' }}>
{item.label}
</li>
))}
</ol>
);
}
Now the list owns the rendering of each row, and nothing depends on how the caller wrote its JSX. When a parent needs to share values with deeply nested children (a selected tab, a theme), use context rather than cloning props onto them.
Typing children in TypeScript
In TypeScript, type children as React.ReactNode. It covers everything React can render: elements, strings, numbers, arrays of those, null, undefined and booleans.
import type { ReactNode } from 'react';
type CardProps = {
title: string;
children: ReactNode;
};
function Card({ title, children }: CardProps) {
return (
<section>
<h3>{title}</h3>
{children}
</section>
);
}
For a render prop, type it as the function it is: children: (on: boolean, toggle: () => void) => ReactNode. Make children optional (children?: ReactNode) when the component also makes sense empty.
Common mistakes
Forgetting to render children. If a component accepts children but never puts {children} in its output, the content silently disappears. Nothing warns you.
Calling children like a function when it is not one. children() only works when the caller passed a function. If a component expects a render prop, say so in its name or docs, or accept a named prop such as render so the intent is clear.
Mutating children. Elements are read only. React freezes them in development, so assigning to children.props throws there. Build new output instead.
Frequently Asked Questions
What is props.children in React?
It is the content written between a component's opening and closing tags. In <Card><p>Hi</p></Card>, the Card function receives the <p> as props.children and decides where to render it.
Is children a special prop?
Only in how you pass it. JSX puts the nested content into a prop called children, but inside the component it is an ordinary prop. You can also pass it explicitly, as in <Card children={<p>Hi</p>} />, and it works the same.
How do I pass more than one block of content to a component?
Use named props for the extra blocks. A prop can hold JSX just like children can, so <Modal title={<h2>Delete?</h2>} footer={<button>OK</button>}>Body</Modal> gives the component three slots.
Should I use Children.map and cloneElement?
Avoid them in new code. They only see the elements written directly between the tags, not what those elements render, so they break as soon as someone wraps a child in another component. Pass an array of data as a prop, or use context, instead.
What type is children in TypeScript?
Use React.ReactNode. It covers everything React can render: elements, strings, numbers, arrays, null, undefined and booleans.