Otra manera de indicar que el componente recibirá children en sus props, es extender la interfaz de esta manera:
interface TabProps extends React.PropsWithChildren {
label: string;
}
Aprende a implementar compound components en React para construir un sistema de tabs modular, flexible y tipado con TypeScript. El componente padre controla el estado, los hijos renderizan el contenido. Un enfoque claro para escalar interfaces sin perder simplicidad.
¿Qué es el patrón compound components en React y cómo se aplica a tabs?
El patrón compound components coordina varios componentes que trabajan juntos. El padre decide qué mostrar y el hijo se enfoca en renderizar. Aquí, el padre es un wrapper llamado Tabs y los hijos son Tab: el primero gestiona el estado local y los segundos muestran label y children.
¿Qué props y tipos se definen?
¿Qué utilidades de React se emplean?
Código del componente hijo Tab:
import React, { ReactNode } from 'react'; interface TabProps { label: string; children: ReactNode; } export const Tab: React.FC<TabProps> = ({ label, children }) => { return ( <div> <em>{label}</em> <span>{children}</span> </div> ); };
¿Cómo se construye el componente tabs con estado y validación de hijos?
El componente Tabs necesita un estado local para saber qué pestaña está activa y una función para actualizarla al hacer clic. Además, convierte los hijos a arreglo y filtra solo elementos válidos de React. Luego, renderiza una lista de labels y un área de contenido.
activeIndex en 0.handleTabClick(index: number) para cambiar el estado.tabElements con Children.toArray(children) y isValidElement.<li> por cada tab con su label.tabElements[activeIndex] dentro de tabContent.Código del componente padre Tabs:
import React, { ReactNode, useState, Children, isValidElement } from 'react'; interface TabsProps { children: ReactNode; } export const Tabs: React.FC<TabsProps> = ({ children }) => { const [activeIndex, setActiveIndex] = useState(0); const handleTabClick = (index: number) => { setActiveIndex(index); }; const tabElements = Children.toArray(children).filter((child) => isValidElement(child) ); return ( <div> <ul> {tabElements.map((child: any, index: number) => ( <li key={index} className={index === activeIndex ? 'tab active' : ''} onClick={() => handleTabClick(index)} > {child.props.label} </li> ))} </ul> <div className={'tabContent'}> {tabElements[activeIndex] as React.ReactNode} </div> </div> ); };
¿Cómo se usa en el componente padre con etiquetas y contenido?
Una vez definidos Tabs y Tab, se reemplaza el contenedor por Tabs y se agregan varios Tab con su label y children. Al hacer clic, cambia el contenido activo sin lógica extra en los hijos.
Ejemplo de uso:
<Tabs> <Tab label="Jirafa">🦒</Tab> <Tab label="Delfín">🐬</Tab> <Tab label="Lagarto">🦎</Tab> </Tabs>
¿Te gustaría aportar mejoras o mostrar tu implementación de tabs con compound components? Comparte tus ideas y tu landing de ejemplos en los comentarios.
Aprende a implementar compound components en React para construir un sistema de tabs modular, flexible y tipado con TypeScript. El componente padre controla el estado, los hijos renderizan el contenido. Un enfoque claro para escalar interfaces sin perder simplicidad.
El patrón compound components coordina varios componentes que trabajan juntos. El padre decide qué mostrar y el hijo se enfoca en renderizar. Aquí, el padre es un wrapper llamado Tabs y los hijos son Tab: el primero gestiona el estado local y los segundos muestran label y children.
Código del componente hijo Tab:
import React, { ReactNode } from 'react';
interface TabProps {
label: string;
children: ReactNode;
}
export const Tab: React.FC<TabProps> = ({ label, children }) => {
return (
<div>
<em>{label}</em>
<span>{children}</span>
</div>
);
};
El componente Tabs necesita un estado local para saber qué pestaña está activa y una función para actualizarla al hacer clic. Además, convierte los hijos a arreglo y filtra solo elementos válidos de React. Luego, renderiza una lista de labels y un área de contenido.
activeIndex en 0.handleTabClick(index: number) para cambiar el estado.tabElements con Children.toArray(children) y isValidElement.<li> por cada tab con su label.tabElements[activeIndex] dentro de tabContent.Código del componente padre Tabs:
import React, { ReactNode, useState, Children, isValidElement } from 'react';
interface TabsProps {
children: ReactNode;
}
export const Tabs: React.FC<TabsProps> = ({ children }) => {
const [activeIndex, setActiveIndex] = useState(0);
const handleTabClick = (index: number) => {
setActiveIndex(index);
};
const tabElements = Children.toArray(children).filter((child) =>
isValidElement(child)
);
return (
<div>
<ul>
{tabElements.map((child: any, index: number) => (
<li
key={index}
className={index === activeIndex ? 'tab active' : ''}
onClick={() => handleTabClick(index)}
>
{child.props.label}
</li>
))}
</ul>
<div className={'tabContent'}>
{tabElements[activeIndex] as React.ReactNode}
</div>
</div>
);
};
Una vez definidos Tabs y Tab, se reemplaza el contenedor por Tabs y se agregan varios Tab con su label y children. Al hacer clic, cambia el contenido activo sin lógica extra en los hijos.
Ejemplo de uso:
<Tabs>
<Tab label="Jirafa">🦒</Tab>
<Tab label="Delfín">🐬</Tab>
<Tab label="Lagarto">🦎</Tab>
</Tabs>
¿Te gustaría aportar mejoras o mostrar tu implementación de tabs con compound components? Comparte tus ideas y tu landing de ejemplos en los comentarios.
Otra manera de indicar que el componente recibirá children en sus props, es extender la interfaz de esta manera:
interface TabProps extends React.PropsWithChildren {
label: string;
}
Veo mi reflejo en la clase, al estudiar inglés se suele perder algo de ortografía como jirafa... pero de resto muy interesante, hay prácticas que uso y no conozco el nombre, solo las copio de otros devs al leer otros repos, muy interesante el ejemplo, gracias
Cuando utilizar type vs interface a la hora de definir los props?
Que buen curso carajo!
Ahora entiendo de donde viene que el codigo de la AI este lleno de emojis :)
Este patrón me parece súper interesanteee y la forma en que accedemos desde Tabs a las props de Tab era algo que nunca habia visto, súper interesante y muy buena clase
¿No entendiste el filtrado del array "tabElements" y el uso de "child is ReactElement"?
Lee las siguientes láminas y entenderás. Lo mismo me pasó a mi.







Si te sale este error:
ReactElement is a type and must be imported using a type-only import when "verbatimModuleSyntax" is enabled
al importart "ReactElement" en el minuto 9:12 del video, es por lo siguiente:
Esto significa que ReactElement es solo un tipo, no un valor en tiempo de ejecución. Cuando verbatimModuleSyntax está activado, TypeScript te obliga a diferenciar entre importaciones de valores y importaciones de tipos.
📢Solución: Importar de la siguiente forma
import type { ReactElement } from "react";


Muchas Gracias
export const CompoundComponents = () => {
return (
<Tabs>
<Tab label="Gato">Miauuu</Tab>
<Tab label="Perro">Wouuu</Tab>
<Tab label="Pollito">Pioo</Tab>
</Tabs>
);
};
```a un estoy procesando como es que Tabs recibió los hijos jejeje Esta super