Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
// Composição baseada em slots: passe componentes como props
function Layout({ header, sidebar, children }: {
header: React.ReactNode;
sidebar: React.ReactNode;
children: React.ReactNode;
}) {
return (
<div className="grid grid-cols-[240px_1fr] grid-rows-[64px_1fr]">
<header className="col-span-2">{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}
// Uso
<Layout
header={<TopNav user={currentUser} />}
sidebar={<SideMenu items={menuItems} />}
>
<Dashboard data={dashboardData} />
</Layout>Quando usar isso: Sempre que precisar de um contêiner, layout ou wrapper reutilizável que deva permanecer agnóstico sobre seu conteúdo. Prefira composição em vez de herança em todos os casos no React.
import { type ReactNode } from "react";
// Um componente Card com slots compostos
interface CardProps {
media?: ReactNode;
actions?: ReactNode;
children: ReactNode;
variant?: "elevated" | "outlined";
}
function Card({ media, actions, children, variant = "outlined" }: CardProps) {
const base = "rounded-lg overflow-hidden";
const styles = variant === "elevated"
? `${base} shadow-lg bg-white`
: `${base} border border-gray-200 bg-white`;
return (
<div className={styles}>
{media && <div className="aspect-video overflow-hidden">{media}</div>}
<div className="p-4">{children}</div>
{actions && (
<div className="px-4 pb-4 flex gap-2 justify-end">{actions}</div>
)}
</div>
);
}
// Especialização através de composição, NÃO herança
function ProductCard({ product }: { product: Product }) {
return (
<Card
variant="elevated"
media={<img src={product.image} alt={product.name} />}
actions={
<>
<button className="btn-secondary">Salvar</button>
<button className="btn-primary">Adicionar ao Carrinho</button>
</>
}
>
<h3 className="font-semibold">{product.name}</h3>
<p className="text-gray-600">${product.price}</p>
</Card>
);
}
interface Product {
name: string;
price: number;
image: string;
}O que isso demonstra:
ReactNode nomeadas (media, actions, children)Card) dentro de um específico do domínio (ProductCard)ProductCard não é uma subclasse de Cardchildren é a primitiva de composição principal - qualquer JSX aninhado dentro de um componente é passado como props.children.ReactNode) permitem injetar conteúdo em posições específicas dentro do layout de um componente.{children} como um placeholder.| Padrão de Prop | Tipo | Propósito |
|---|---|---|
children | ReactNode | Slot padrão para conteúdo aninhado |
Slot nomeado (ex. header) | ReactNode | Posicionamento explícito de conteúdo em uma área específica |
| Callback de renderização | (data: T) => ReactNode | Conteúdo que precisa de acesso ao estado interno (ver render-props) |
| Prop de componente | React.ComponentType<P> | Injetar um componente completo a ser instanciado internamente |
Padrão de injeção de componente - passe um tipo de componente em vez de um elemento renderizado:
interface ListProps<T> {
items: T[];
renderItem: React.ComponentType<{ item: T }>;
}
function List<T>({ items, renderItem: Item }: ListProps<T>) {
return (
<ul>
{items.map((item, i) => (
<li key={i}><Item item={item} /></li>
))}
</ul>
);
}Composição de Provider - achate múltiplos providers de contexto aninhados:
function AppProviders({ children }: { children: ReactNode }) {
return (
<ThemeProvider>
<AuthProvider>
<QueryProvider>
{children}
</QueryProvider>
</AuthProvider>
</ThemeProvider>
);
}ReactNode para slots que aceitam qualquer conteúdo renderizável (strings, elementos, fragmentos, null).React.ComponentType<P> quando precisar passar um componente que será instanciado com props específicas.ReactElement apenas quando precisar restringir a elementos JSX reais (excluindo strings e números).List<T>) preserva a segurança de tipo através da fronteira do componente.Uso excessivo de children para múltiplos slots - Ao passar tudo como children, você perde o controle sobre o posicionamento. Correção: Use props ReactNode nomeadas para áreas de conteúdo distintas.
Prop drilling através de camadas compostas - Composição profunda pode levar a passar props através de muitos níveis. Correção: Use contexto para preocupações verdadeiramente transversais e mantenha a composição superficial.
Quebra da memoização com slots JSX inline - Passar <Component /> inline como uma prop cria uma nova referência de elemento a cada renderização. Correção: Eleve o conteúdo do slot estático para fora da renderização ou envolva com useMemo para árvores caras.
Confundir especialização com configuração - Criar um novo componente apenas para definir algumas props é aceitável; criar um wrapper que re-expõe todas as props originais é um sinal de que você precisa de composição, não de wrapping. Correção: Use o componente original diretamente e passe as props no local de chamada.
| Abordagem | Compromisso |
|---|---|
| Composição (slots) | Mais flexível; requer mais JSX no local de chamada |
| Props de configuração (strings de variante) | Menos flexível, mas API mais simples para casos comuns |
| Render props | Mais poder quando o conteúdo do slot precisa de estado interno |
| Componentes de ordem superior (Higher-Order Components) | Adiciona comportamento sem alterar a API, mas mais difícil de depurar |
| Herança | Não recomendado no React - quebra com componentes de função |
ReactNode nomeadas (como header, sidebar, actions) quando o conteúdo precisar ser posicionado em locais específicos dentro de um layout.children sozinho apenas quando houver uma única área de conteúdo.children sozinho não pode fornecer.// Elemento renderizado: você passa JSX
<Card media={<img src={url} alt="foto" />} />
// Injeção de componente: você passa um tipo de componente
<List items={data} renderItem={ProductRow} />function AppProviders({ children }: { children: ReactNode }) {
return (
<ThemeProvider>
<AuthProvider>
<QueryProvider>
{children}
</QueryProvider>
</AuthProvider>
</ThemeProvider>
);
}<AppProviders> no local de chamada.ReactNode aceita qualquer conteúdo renderizável: strings, números, elementos, fragmentos e null.ReactElement restringe apenas a elementos JSX reais, excluindo strings e números.React.ComponentType<P> é um tipo de componente (função ou classe) que pode ser instanciado com props do tipo P.interface ListProps<T> {
items: T[];
renderItem: React.ComponentType<{ item: T }>;
}
function List<T>({ items, renderItem: Item }: ListProps<T>) {
return <ul>{items.map((item, i) => <li key={i}><Item item={item} /></li>)}</ul>;
}T do array items e o impõe em renderItem.item dentro do componente de renderização.<Component /> inline como uma prop cria uma nova referência de elemento React a cada renderização.React.memo, a nova referência fará com que ele re-renderize.useMemo.(data: T) => ReactNode) quando o conteúdo do slot precisar de acesso ao estado interno do componente pai.ReactNode simples quando o conteúdo for independente do estado interno do componente.ReactNode e atualize apenas quando necessário.interface LayoutProps {
header: ReactNode;
sidebar: ReactNode;
children: ReactNode;
}
function Layout({ header, sidebar, children }: LayoutProps) {
return (
<div>
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}ReactNode.children é uma prop padrão do React que recebe JSX aninhado.Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥