Menu

La prop children en React: envoltorios y slots

children es la prop que contiene lo que pones entre las etiquetas de apertura y cierre de un componente. Aprende a construir componentes envoltorio y de layout con ella, pasar varios huecos como props con nombre, usar children como función, y cuándo la API Children es la herramienta equivocada.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

En React, children es la prop que contiene lo que escribes entre las etiquetas de apertura y cierre de un componente. Un componente que renderiza {children} en algún lugar de su salida se convierte en un envoltorio: él aporta el marco y quien lo usa aporta el contenido.

Ambas tarjetas comparten un mismo borde, un mismo padding y un mismo estilo de título, pero cada una contiene un contenido completamente distinto. Agrega una tercera <Card title="Help"> con un enlace dentro y recibe el mismo marco sin esfuerzo.

Cómo llega children al componente

JSX convierte el contenido anidado en una prop. Estas dos líneas producen el mismo elemento:

<Card title="Profile"><p>Ada</p></Card>

<Card title="Profile" children={<p>Ada</p>} />

Así que children es una prop normal con una forma especial de pasarse. La desestructuras como cualquier otra (function Card({ children })) o la lees como props.children. La página sobre props cubre las props en general.

Lo que llega en children depende de lo que escribiste entre las etiquetas. Un solo hijo llega como ese hijo. Varios hijos llegan como un array. El texto llega como una cadena, y nada en absoluto llega como undefined.

La consola muestra object, array of 2, string y undefined. Rara vez necesitas preocuparte: renderizar {children} maneja todos estos casos, y null, undefined, true y false no renderizan nada. La forma solo importa si intentas inspeccionar o cambiar children, que es justo de lo que advierte la última sección.

Componentes de layout

El uso más común de children es un componente de layout: el armazón de una página, un layout con barra lateral, una columna centrada. El componente es dueño de la estructura y el espaciado, y cada página que lo usa pasa su propio contenido.

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>
    );
}

A PageLayout no le importa ni sabe qué contiene una página "acerca de". Esa separación es la idea: cambias el encabezado una vez y cada página envuelta en el layout lo recibe.

Varios huecos con props con nombre

children es un hueco. Cuando un componente necesita contenido en más de un lugar, pasa las piezas extra como props con nombre. Cualquier prop puede contener JSX, así que un modal puede recibir un title, un footer y su cuerpo como children.

El armazón del modal controla los bordes, el padding y el orden de las tres áreas, y quien lo usa llena cada una. Otros frameworks los llaman slots; en React son simplemente props. El <>...</> alrededor de los dos botones es un fragmento, que los agrupa sin agregar un elemento extra. Un diálogo real también se renderizaría en document.body con un portal para quedar por encima de la página.

Mueve el <p> a la prop title y el texto del cuerpo aparece en el encabezado: el componente decide dónde va cada prop, quien lo usa solo decide qué va dentro.

Children como función

A veces el envoltorio es dueño de cierto estado y quien lo usa debe decidir cómo dibujarlo. En lugar de un elemento, quien lo usa pasa una función como children, y el envoltorio la llama con los valores. Este patrón se llama render prop.

Un Toggle dibuja un botón y el otro una casilla, a partir de la misma lógica. Las render props eran la forma principal de compartir lógica con estado antes de los hooks. Hoy un hook personalizado como useToggle() suele leerse mejor, pero todavía verás render props en librerías de listas, formularios y animación.

La API Children y cloneElement

React exporta un objeto Children con utilidades para recorrer children: Children.map, Children.forEach, Children.count, Children.toArray y Children.only. Junto con cloneElement, que copia un elemento con props nuevas, permiten que un padre inspeccione y cambie lo que recibió. El equipo de React las clasifica como APIs heredadas: siguen funcionando, pero el código nuevo debería evitarlas.

La razón es que solo ven los elementos escritos directamente entre las etiquetas. No pueden ver lo que esos elementos renderizan.

La lista muestra cuatro elementos, pero la consola dice Children.count = 3, porque TwoMore es un solo hijo sin importar cuántos elementos renderice. cloneElement también le pasa la prop style a TwoMore, que la ignora, así que el tercer y el cuarto elemento no quedan alternados. Cualquiera que refactorice unos cuantos elementos <li> en un componente rompe el padre sin tocarlo.

La solución es dejar de meterse en children y pasar datos en su lugar:

function NumberedList({ items }) {
    return (
        <ol>
            {items.map((item, i) => (
                <li key={item.id} style={{ color: i % 2 ? 'gray' : 'black' }}>
                    {item.label}
                </li>
            ))}
        </ol>
    );
}

Ahora la lista es dueña del renderizado de cada fila, y nada depende de cómo escribió su JSX quien la usa. Cuando un padre necesita compartir valores con hijos muy anidados (una pestaña seleccionada, un tema), usa contexto en lugar de clonar props sobre ellos.

Tipar children en TypeScript

En TypeScript, tipa children como React.ReactNode. Cubre todo lo que React puede renderizar: elementos, cadenas, números, arrays de esos, null, undefined y 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 una render prop, tipala como la función que es: children: (on: boolean, toggle: () => void) => ReactNode. Haz children opcional (children?: ReactNode) cuando el componente también tenga sentido vacío.

Errores comunes

Olvidar renderizar children. Si un componente acepta children pero nunca pone {children} en su salida, el contenido desaparece en silencio. Nada te avisa.

Llamar a children como función cuando no lo es. children() solo funciona cuando quien lo usa pasó una función. Si un componente espera una render prop, dilo en su nombre o en su documentación, o acepta una prop con nombre como render para que la intención quede clara.

Mutar children. Los elementos son de solo lectura. React los congela en desarrollo, así que asignar a children.props lanza un error ahí. Construye una salida nueva en su lugar.

Preguntas frecuentes

¿Qué es props.children en React?

Es el contenido escrito entre las etiquetas de apertura y cierre de un componente. En <Card><p>Hi</p></Card>, la función Card recibe el <p> como props.children y decide dónde renderizarlo.

¿children es una prop especial?

Solo en la forma de pasarla. JSX pone el contenido anidado en una prop llamada children, pero dentro del componente es una prop normal. También puedes pasarla explícitamente, como en <Card children={<p>Hi</p>} />, y funciona igual.

¿Cómo paso más de un bloque de contenido a un componente?

Usa props con nombre para los bloques extra. Una prop puede contener JSX igual que children, así que <Modal title={<h2>Delete?</h2>} footer={<button>OK</button>}>Body</Modal> le da al componente tres huecos.

¿Debo usar Children.map y cloneElement?

Evítalos en código nuevo. Solo ven los elementos escritos directamente entre las etiquetas, no lo que esos elementos renderizan, así que se rompen en cuanto alguien envuelve un hijo en otro componente. En su lugar, pasa un array de datos como prop o usa contexto.

¿Qué tipo tiene children en TypeScript?

Usa React.ReactNode. Cubre todo lo que React puede renderizar: elementos, cadenas, números, arrays, null, undefined y booleanos.

Ilustración de los lenguajes de programación de Coddy

Aprende a programar con Coddy

COMENZAR