Що таке children у React
children - це спеціальний проп у React, який автоматично отримує всі JSX-елементи, вкладені між відкриваючим і закриваючим тегами компонента.
Теорія
TL;DR
childrenзаповнюється автоматично з вкладеного JSX - передавати його явно не потрібно- Компонент як рамка для картини: рамка - це компонент, картина - це
children - Використовуй
children, коли компонент обгортає контент; звичайні пропси - для конкретних іменованих даних - У TypeScript завжди оголошуй
children: React.ReactNodeв інтерфейсі
Швидкий приклад
function Card({ children }) {
return <div className="card">{children}</div>;
}
// Все між тегами стає children
<Card>
<h1>Заголовок</h1>
<p>Опис</p>
</Card>
// Card рендерить обидва елементи всередині div
// children = <h1>Заголовок</h1><p>Опис</p>Коли ти вкладаєш JSX між тегами компонента, React збирає це і передає як проп children автоматично. Компоненту не потрібно знати, що всередині. Він просто рендерить там, де стоїть {children}.
Як це працює
React перетворює JSX на виклики функцій. <Card><h1>Title</h1></Card> стає Card({ children: <h1>Title</h1> }). Якщо вкладаєш кілька елементів, children стає масивоподібним об'єктом. Один елемент - children це сам елемент. Кілька - це масив. Ця асиметрія є джерелом поширеної помилки (дивись нижче).
Коли використовувати
Використовуй children, коли:
- Будуєш layout-обгортки:
Page,Container,Section - Створюєш UI-контейнери:
Card,Modal,Sidebar,Drawer - Завдання компонента - обгорнути контент стилями або поведінкою
Пропускай children, коли потрібно кілька іменованих слотів. Dialog з окремими header, body і footer зручніше описати через явні пропси, а не один проп children.
Типові помилки
Припускати, що children завжди масив:
// Падає, якщо передати один елемент
function List({ children }) {
return <ul>{children.map(item => <li>{item}</li>)}</ul>;
}
<List><span>Один елемент</span></List> // children - це span, а не масивВиправлення: використовуй React.Children.toArray():
function List({ children }) {
const items = React.Children.toArray(children);
return <ul>{items.map((item, i) => <li key={i}>{item}</li>)}</ul>;
}Вважати children рядком:
// Працює для тексту, падає для JSX
function Button({ children }) {
return <button>{children.toUpperCase()}</button>; // Падає якщо children = <Icon />
}children може бути рядком, числом, JSX-елементом, масивом або фрагментом. Ніколи не припускай, що це просто рядок.
Забути типи в TypeScript:
// TypeScript-помилка - children нема в інтерфейсі
interface Props {
title: string;
}
function Card({ title, children }: Props) { // TS скаржиться
return <div>{title}{children}</div>;
}
// Виправлення
interface Props {
title: string;
children: React.ReactNode;
}Де зустрічається
- Next.js layouts -
_app.tsxотримуєchildrenі обгортає кожну сторінку спільним UI - React Router -
<Route>використовуєchildrenдля рендеру вкладених маршрутів - Material-UI -
<Box>,<Card>,<Dialog>- всі приймаютьchildrenдля гнучкого контенту - Compound components -
<Tabs>і<TabPanel>координуються черезchildren
Питання до теми
Q: Яка різниця між children і звичайним пропом, наприклад content?
A: children заповнюється автоматично з вкладеного JSX - писати children={...} не потрібно. Звичайний проп вимагає явної передачі. children - для обгортання контенту, звичайні пропси - для конкретних іменованих даних.
Q: Чи можна вкласти кілька елементів?
A: Так. Вкладай скільки завгодно - children стане масивоподібним об'єктом з усіма ними. Для безпечного перебору використовуй React.Children.toArray().
Q: Як типізувати children у TypeScript?
A: Використовуй children: React.ReactNode для максимальної гнучкості. Приймає JSX-елементи, рядки, числа, фрагменти і null. children: React.ReactElement - тільки якщо строго потрібен JSX без рядків.
Q: (Senior) Як додати проп до кожного дочірнього елемента?
A: React.Children.map() разом з React.cloneElement(). Завжди перевіряй через React.isValidElement(), щоб не зачіпати рядки і числа:
function WithProp({ children, disabled }) {
return React.Children.map(children, child =>
React.isValidElement(child)
? React.cloneElement(child, { disabled })
: child
);
}Приклади
Layout-компонент
function Page({ children, title }) {
return (
<div className="page">
<header>
<h1>{title}</h1>
</header>
<main className="page-content">
{children}
</main>
<footer>© 2026</footer>
</div>
);
}
export default function App() {
return (
<Page title="Дашборд">
<section>
<h2>Ласкаво просимо</h2>
<p>Твоя статистика тут</p>
</section>
<section>
<h2>Остання активність</h2>
</section>
</Page>
);
}
// Обидві секції рендеряться всередині main.page-content
// Page не знає, що всередині - просто надає структуруPage відповідає за header, footer і розмітку. Контент змінюється без змін у Page. У більшості Next.js-проектів саме цей паттерн у кореневому layout - перше місце, де розробники бачать children у дії. Ось у чому цінність такого розподілу.
Фільтрація дочірніх елементів
function List({ children, filter }) {
const items = React.Children.toArray(children);
const filtered = filter
? items.filter(item => item.props.active !== false)
: items;
return (
<ul className="list">
{filtered.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
<List filter={true}>
<span active={true}>Елемент 1</span>
<span active={false}>Елемент 2</span>
<span active={true}>Елемент 3</span>
</List>
// Рендерить тільки Елемент 1 і Елемент 3React.Children.toArray() нормалізує children у звичайний масив незалежно від того, чи це один елемент, масив або фрагмент. Без нього .filter() впав би на одному дочірньому елементі.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.