wandres.dev
CHILDREN() · resolver hijos

children en Solid frente a slots y React

El salto mental entre los hijos-descriptor de React, los slots de Vue, los snippets de Svelte 5 y los hijos-nodo de Solid, y como traducir cada patron entre mundos.

⏱ 16 min

Casi todos llegamos a children() arrastrando el modelo mental de otro framework: React.Children, los slots de Vue, los let: de Svelte. Ese equipaje es la principal fuente de confusión. Este nivel hace explícitas las diferencias —qué es un hijo en cada mundo— para que traduzcas patrones sin arrastrar suposiciones falsas. Cerrar esta comparación es cerrar tu comprensión del helper.

🎯 Al terminar esta lección sabrás
  • Distinguir hijo-descriptor (React) de hijo-nodo (Solid).
  • Situar los slots de Vue y los snippets de Svelte 5 en el mapa.
  • Reconocer que scoped slots ≈ hijos como función ≈ render props.
  • Traducir “named slots” y “clonar props” al idioma de Solid.

React: hijos como descriptores

En React, props.children es un descriptor: un objeto plano (o un array de ellos) que describe qué renderizar, sin haberlo renderizado aún. Por eso puedes recorrerlo con React.Children.map, contarlo con React.Children.count y, sobre todo, clonarlo con cloneElement para inyectar props. Los hijos se recrean en cada render, pero son baratos e inertes: mirarlos mil veces no cuesta nada ni tiene efectos. Todo el vocabulario de “transformar hijos” nace de que son datos.

Esa maleabilidad tiene letra pequeña. cloneElement solo alcanza el primer nivel de hijos; si envuelves o anidas, la inyección no baja sola. Depende de que las keys se preserven y de que el hijo espere justo las props que le inyectas: un acoplamiento implícito que se rompe en silencio al refactorizar. Es potente y, a la vez, notoriamente frágil, y funciona hasta que alguien mete un fragmento o un componente intermedio por el medio.

Vue y Svelte: slots y su evolución

Vue usa slots: contenido que el padre proyecta en huecos nombrados. Los named slots (<slot name="header">) organizan zonas; los scoped slots invierten el flujo y dejan que el hijo pase datos al contenido del padre (v-slot="datos"). Bajo el capó, un slot es una función que devuelve VNodes.

El slot por defecto es el contenido sin nombre; los nombrados se declaran con #nombre en el consumidor. La clave conceptual es que un slot no es contenido ya renderizado: es una función que el componente decide cuándo y con qué datos invocar. De ahí sale el control fino de Vue sobre proyección, orden y renderizado condicional del contenido ajeno.

Svelte vivió una mudanza. Hasta Svelte 4 había <slot> y props de slot con let:valor. Svelte 5 —el estándar en 2026— los sustituyó por snippets y {@render ...}: fragmentos de marcado de primera clase, que aceptan argumentos y se pasan como props. Es un salto conceptual que acerca Svelte a las render props.

ℹ️
Todos convergen hacia la función

Fíjate en la dirección del ecosistema. Vue añadió scoped slots, Svelte 5 sustituyó slots por snippets con argumentos, y React lleva años con render props. Todos redescubren la misma primitiva: pasar una función y llamarla con datos. Solid la tiene de serie como “hijos como función”, sin sintaxis especial, porque en Solid una función que devuelve JSX ya es un ciudadano de primera. La convergencia no es casual: es la forma más honesta de que un padre entregue datos a un hijo sin reescribirlo desde fuera.

⚛️

React

Hijo = descriptor inerte. Herramientas: Children.map, cloneElement. Se recrea cada render. Inyección por clonado.

🟩

Vue

Hijo = slot (función a VNodes). Named y scoped slots. Los scoped slots pasan datos del hijo al padre.

🔥

Svelte 5

Hijo = snippet de primera clase. Se invoca con {@render} y admite argumentos. Cercano a render props.

🧬

Solid

Hijo = nodo DOM real ya materializado. Sin clonado. Se comunica por context o funciones. children() memoiza.

Solid: realidad materializada

En Solid, un hijo no es un plano ni una función a VNodes: al evaluarse ya es un nodo DOM (o una cadena, o un número). El componente corre una vez; no hay renders sucesivos que recreen los hijos. De ahí las tres consecuencias que has visto en este nivel: leer props.children dos veces rehace el trabajo; no hay cloneElement porque no hay descriptor; y children() existe para sostener esos nodos como un valor estable y memoizado, algo que React no necesita porque sus hijos ya son datos inertes.

Conviene decir qué no tiene Solid, para cerrar falsas expectativas: no hay React.Children.count, no hay cloneElement, no hay sintaxis de slots nombrados. En su lugar tienes children().toArray().length, tienes context y funciones, y tienes props de tipo JSX.Element para las zonas nombradas. Menos API, el mismo poder expresivo, y ningún intermediario entre tu intención y el DOM.

📝
Zonas nombradas, sin sintaxis nueva

Cuando eches de menos los named slots, recuerda: en Solid una zona es una prop de tipo JSX.Element. header, footer y aside son props como titulo o color. No aprendes una sintaxis de slots; reutilizas el modelo de props que ya dominas, ahora transportando marcado en lugar de cadenas, y con toda su reactividad de grano fino intacta.

mindmap
root((hijos entre frameworks))
  React
    descriptores inertes
    cloneElement inyecta
    se recrean en cada render
  Vue
    slots con nombre
    scoped slots pasan datos
  Svelte 5
    snippets primera clase
    render tag
  Solid
    nodos DOM reales
    children helper memoiza
    context o funcion hijo

Traducir patrones entre mundos

  • “Named slots” de Vue/Svelte → en Solid, props que contienen JSX. Un header y un footer son simplemente props de tipo JSX.Element, no una sintaxis especial.
  • Scoped slots / snippets con argumentoshijos como función: props.children(datos). Misma idea, misma firma.
  • cloneElement para inyectar propscontext (comunicación por el grafo) o hijos como función. No hay traducción literal, y es buena señal.
  • React.Children.map para envolverchildren().toArray().map(...), operando sobre nodos reales.
  • useContext de React para estado compartidocreateContext y useContext de Solid, pero reactivo de grano fino: el consumidor se actualiza sin re-render.
  • Fallback de slot (contenido por defecto) → un valor por defecto directo: props.children ?? <Vacio />, o mergeProps para defaults reactivos.

La misma idea de “inyectar datos” se ve casi idéntica en los tres idiomas, y comparar su forma revela el contrato de cada uno:

// React (render prop)
<Mouse>{(pos) => <Cursor at={pos} />}</Mouse>

// Solid (hijo como funcion), identico en espiritu
<Mouse>{(pos) => <Cursor at={pos()} />}</Mouse>

// Vue (scoped slot), en su plantilla:
// <Mouse v-slot="pos"><Cursor :at="pos" /></Mouse>

La diferencia entre la render prop de React y el hijo-función de Solid es reveladora: en React pos es un valor del render actual; en Solid pos suele ser un getter que llamas —pos()— para leer el valor vigente. Misma forma, distinto contrato: React te da la foto del render; Solid te da la conexión viva. Traducir entre ambos no es cambiar la sintaxis, es pasar de “valor” a “señal”.

// "named slots" en Solid: solo props con JSX
function Layout(props: { header: JSX.Element; children: JSX.Element }) {
  return (
    <>
      <header>{props.header}</header>
      <main>{props.children}</main>
    </>
  );
}

// uso
<Layout header={<h1>Panel</h1>}>Cuerpo de la pagina</Layout>;

Ese Layout es todo lo que Solid necesita para “named slots”: props tipadas como JSX.Element. No hay magia de compilador ni nombres reservados; una zona con nombre es, literalmente, una prop con ese nombre. Y como es una prop normal, hereda todo el modelo del nivel 7: es un getter, es reactiva, se puede reenviar. Los slots de Vue son una construcción del framework; en Solid, las “zonas” son datos de primera clase que ya sabes manejar.

📝
El helper no cruza el puente por ti

children() te da los hijos de Solid como valor estable, pero no los convierte en descriptores manipulables al estilo React. Si vienes buscando “el React.Children de Solid”, ajusta la expectativa: lo que obtienes son nodos, no planos. Puedes contarlos, envolverlos y decorarlos (nivel 8.3), pero no reconfigurarlos con props nuevas. Para eso, comunica con context o entrega el control con una función.

Deja de preguntar cómo transformar tus hijos

La frase que resume el nivel entero: en React preguntas “cómo transformo mis hijos”; en Solid preguntas “cómo me comunico con ellos” o “qué les entrego”. Ese giro —de transformación a comunicación— no es estético, es estructural, porque nace de qué es un hijo en cada motor. Donde React te da datos que puedes reescribir a placer y un reconciliador que los materializa luego, Solid te da nodos ya vivos y ningún reconciliador, así que la manipulación retrospectiva pierde sentido y la coordinación en tiempo real —context, señales, funciones— lo gana todo. Los scoped slots de Vue y los snippets de Svelte 5 revelan que el resto del ecosistema converge hacia la misma idea que Solid lleva de serie: pasar una función y llamarla con datos es más honesto que clonar y reescribir. Y children(), la pieza con la que abrimos el nivel, encuentra aquí su sentido último: no es el React.Children de Solid, es la herramienta que reconcilia “quiero tratar los hijos como un valor” con un motor donde los hijos nunca fueron un valor, sino materia viva. Dominarla es haber cambiado de modelo mental, no solo de sintaxis.

⚔️ Traduce entre frameworks
  1. Toma un componente React con cloneElement y reescríbelo en Solid con context. Anota qué props inyectadas pasan a ser lectura de context.
  2. Convierte un scoped slot mental de Vue en un hijo como función de Solid.
  3. Implementa “named slots” con props header y footer de tipo JSX.Element.
  4. Explica en dos frases por qué children() no es el equivalente de React.Children, sino la respuesta a un problema que React no tiene.