Skip to main content

Що таке children у React

children - це спеціальний проп у React, який автоматично отримує всі JSX-елементи, вкладені між відкриваючим і закриваючим тегами компонента.

Теорія

TL;DR

  • children заповнюється автоматично з вкладеного JSX - передавати його явно не потрібно
  • Компонент як рамка для картини: рамка - це компонент, картина - це children
  • Використовуй children, коли компонент обгортає контент; звичайні пропси - для конкретних іменованих даних
  • У TypeScript завжди оголошуй children: React.ReactNode в інтерфейсі

Швидкий приклад

tsx
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 завжди масив:

tsx
// Падає, якщо передати один елемент function List({ children }) { return <ul>{children.map(item => <li>{item}</li>)}</ul>; } <List><span>Один елемент</span></List> // children - це span, а не масив

Виправлення: використовуй React.Children.toArray():

tsx
function List({ children }) { const items = React.Children.toArray(children); return <ul>{items.map((item, i) => <li key={i}>{item}</li>)}</ul>; }

Вважати children рядком:

tsx
// Працює для тексту, падає для JSX function Button({ children }) { return <button>{children.toUpperCase()}</button>; // Падає якщо children = <Icon /> }

children може бути рядком, числом, JSX-елементом, масивом або фрагментом. Ніколи не припускай, що це просто рядок.

Забути типи в TypeScript:

tsx
// 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(), щоб не зачіпати рядки і числа:

tsx
function WithProp({ children, disabled }) { return React.Children.map(children, child => React.isValidElement(child) ? React.cloneElement(child, { disabled }) : child ); }

Приклади

Layout-компонент

tsx
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 у дії. Ось у чому цінність такого розподілу.

Фільтрація дочірніх елементів

tsx
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 і Елемент 3

React.Children.toArray() нормалізує children у звичайний масив незалежно від того, чи це один елемент, масив або фрагмент. Без нього .filter() впав би на одному дочірньому елементі.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?