Children and composition
Sometimes a component needs not a value but a whole piece of markup: a card with a border that can contain anything. Such a piece is passed between the opening and closing tag, and the component receives it in a special prop, children. Building an interface from components nested inside each other like this is called composition.
A wrapper component
Create src/components/Card.jsx:
// src/components/Card.jsx
export default function Card({ children }) {
return <div className="card">{children}</div>;
}children is whatever ends up between the Card tags. The component renders it inside the border and knows nothing about the content.
Using it
// src/App.jsx
import Card from './components/Card.jsx';
export default function App() {
return (
<Card>
<h2>Buy milk</h2>
<p>By the evening, at the shop nearby.</p>
</Card>
);
}The page shows a block with the class card containing a heading and a paragraph. Add the rule .card { border: 1px solid #ccc; padding: 16px; } to src/index.css to see the border.
A wrapper with props
children combines with ordinary props. For example, a panel with a title — src/components/Panel.jsx:
// src/components/Panel.jsx
export default function Panel({ title, children }) {
return (
<section className="card">
<h2>{title}</h2>
{children}
</section>
);
}// src/App.jsx
import Panel from './components/Panel.jsx';
import TodoItem from './components/TodoItem.jsx';
export default function App() {
return (
<Panel title="Today">
<ul>
<TodoItem todo={{ id: 1, text: 'Buy milk', done: false }} />
<TodoItem todo={{ id: 2, text: 'Call Oleg', done: true }} />
</ul>
</Panel>
);
}The "Today" panel shows a list of two tasks. The same panel can be used for a "Tomorrow" block with different content.
A page frame
children is handy for a shared frame: the header and footer stay the same, the middle changes. Create src/components/Layout.jsx:
// src/components/Layout.jsx
export default function Layout({ children }) {
return (
<>
<header>My tasks</header>
<main>{children}</main>
<footer>2026</footer>
</>
);
}In App, all page content is wrapped in Layout. Later, in the routing chapter, a shared page layout plays a similar role.
What to use when
| You need to pass | Way |
|---|---|
| A value: text, number, object | An ordinary prop |
| A piece of markup inside the component | children |
| Several pieces of markup in different places | Several props holding JSX, e.g. header and footer |
Common mistake
A wrapper component must render children itself. If you forget, the nested markup disappears without any error:
// Wrong: the content between the Card tags does not appear.
export default function Card() {
return <div className="card"></div>;
}Accept children in the parameters and put {children} where it belongs in the markup.