No React, children é a prop que guarda o que você escreve entre a tag de abertura e a de fechamento de um componente. Um componente que renderiza {children} em algum ponto da saída vira um wrapper: ele fornece a moldura, e quem o usa fornece o conteúdo.
Os dois cards compartilham a mesma borda, o mesmo padding e o mesmo estilo de título, mas cada um guarda um conteúdo completamente diferente. Adicione um terceiro <Card title="Help"> com um link dentro e ele ganha a mesma moldura de graça.
Como children chega ao componente
O JSX transforma o conteúdo aninhado em uma prop. Estas duas linhas produzem o mesmo elemento:
<Card title="Profile"><p>Ada</p></Card>
<Card title="Profile" children={<p>Ada</p>} />
Então children é uma prop comum com um jeito especial de ser passada. Você a desestrutura como qualquer outra (function Card({ children })) ou a lê como props.children. A página sobre props trata das props em geral.
O que chega em children depende do que você escreveu entre as tags. Um filho chega como esse filho. Vários filhos chegam como um array. Texto chega como string, e nada chega como undefined.
O console mostra object, array of 2, string e undefined. Raramente você precisa se preocupar: renderizar {children} lida com todos esses casos, e null, undefined, true e false não renderizam nada. O formato só importa se você tentar inspecionar ou alterar children, que é justamente o que a última seção desaconselha.
Componentes de layout
O uso mais comum de children é um componente de layout: a estrutura de uma página, um layout com barra lateral, uma coluna centralizada. O componente é dono da estrutura e do espaçamento, e cada página que o usa passa o próprio conteúdo.
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 não sabe nem quer saber o que uma página "sobre" contém. Essa separação é o objetivo: mude o cabeçalho uma vez e todas as páginas envolvidas pelo layout recebem a mudança.
Vários slots com props nomeadas
children é um slot. Quando um componente precisa de conteúdo em mais de um lugar, passe as partes extras como props nomeadas. Qualquer prop pode guardar JSX, então um modal pode receber um title, um footer e o corpo como children.
A estrutura do modal controla as bordas, o padding e a ordem das três áreas, e quem o usa preenche cada uma. Outros frameworks chamam isso de slots; no React são só props. O <>...</> em volta dos dois botões é um fragment, que os agrupa sem adicionar um elemento extra. Um diálogo real também seria renderizado em document.body com um portal, para ficar acima da página.
Mova o <p> para a prop title e o texto do corpo aparece no cabeçalho: o componente decide para onde vai cada prop, quem o usa só decide o que vai dentro dela.
Children como função
Às vezes o wrapper é dono de algum estado e quem o usa deve decidir como desenhá-lo. Em vez de um elemento, quem usa passa uma função como children, e o wrapper a chama com os valores. Esse padrão se chama render prop.
Um Toggle desenha um botão e o outro um checkbox, a partir da mesma lógica. Render props eram a principal forma de compartilhar lógica com estado antes dos hooks. Hoje um hook personalizado como useToggle() costuma ficar mais legível, mas você ainda vai ver render props em bibliotecas de listas, formulários e animação.
A API Children e cloneElement
O React exporta um objeto Children com auxiliares para percorrer children: Children.map, Children.forEach, Children.count, Children.toArray e Children.only. Junto com cloneElement, que copia um elemento com novas props, eles permitem que um pai inspecione e altere o que recebeu. A equipe do React os classifica como APIs legadas: ainda funcionam, mas código novo deve evitá-los.
O motivo é que eles só enxergam os elementos escritos diretamente entre as tags. Não conseguem ver o que esses elementos renderizam.
A lista mostra quatro itens, mas o console diz Children.count = 3, porque TwoMore é um filho só, não importa quantos itens ele renderize. cloneElement também entrega a prop style para TwoMore, que a ignora, então o terceiro e o quarto itens não ficam listrados. Qualquer pessoa que transforme alguns elementos <li> em um componente quebra o pai sem tocar nele.
A solução é parar de mexer em children e passar dados:
function NumberedList({ items }) {
return (
<ol>
{items.map((item, i) => (
<li key={item.id} style={{ color: i % 2 ? 'gray' : 'black' }}>
{item.label}
</li>
))}
</ol>
);
}
Agora a lista é dona da renderização de cada linha, e nada depende de como quem a usa escreveu o JSX. Quando um pai precisa compartilhar valores com filhos profundamente aninhados (uma aba selecionada, um tema), use context em vez de clonar props para eles.
Tipando children no TypeScript
No TypeScript, tipe children como React.ReactNode. Ele cobre tudo o que o React consegue renderizar: elementos, strings, números, arrays dessas coisas, null, undefined e booleanos.
import type { ReactNode } from 'react';
type CardProps = {
title: string;
children: ReactNode;
};
function Card({ title, children }: CardProps) {
return (
<section>
<h3>{title}</h3>
{children}
</section>
);
}
Para uma render prop, tipe-a como a função que ela é: children: (on: boolean, toggle: () => void) => ReactNode. Deixe children opcional (children?: ReactNode) quando o componente também fizer sentido vazio.
Erros comuns
Esquecer de renderizar children. Se um componente aceita children mas nunca coloca {children} na saída, o conteúdo some sem aviso. Nada avisa você.
Chamar children como função quando não é uma. children() só funciona quando quem usa passou uma função. Se um componente espera uma render prop, deixe isso claro no nome ou na documentação, ou aceite uma prop nomeada como render para a intenção ficar evidente.
Mutar children. Elementos são somente leitura. O React os congela em desenvolvimento, então atribuir a children.props lança um erro ali. Construa uma nova saída no lugar.
Perguntas frequentes
O que é props.children no React?
É o conteúdo escrito entre a tag de abertura e a de fechamento de um componente. Em <Card><p>Hi</p></Card>, a função Card recebe o <p> como props.children e decide onde renderizá-lo.
children é uma prop especial?
Só no jeito de passar. O JSX coloca o conteúdo aninhado em uma prop chamada children, mas dentro do componente ela é uma prop comum. Você também pode passá-la explicitamente, como em <Card children={<p>Hi</p>} />, e funciona igual.
Como passo mais de um bloco de conteúdo para um componente?
Use props nomeadas para os blocos extras. Uma prop pode guardar JSX assim como children, então <Modal title={<h2>Delete?</h2>} footer={<button>OK</button>}>Body</Modal> dá três slots ao componente.
Devo usar Children.map e cloneElement?
Evite em código novo. Eles só enxergam os elementos escritos diretamente entre as tags, não o que esses elementos renderizam, então quebram assim que alguém envolve um filho em outro componente. Em vez disso, passe um array de dados como prop ou use context.
Qual é o tipo de children no TypeScript?
Use React.ReactNode. Ele cobre tudo o que o React consegue renderizar: elementos, strings, números, arrays, null, undefined e booleanos.