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.
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.
- 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.
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.
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 hijoTraducir patrones entre mundos
- “Named slots” de Vue/Svelte → en Solid, props que contienen JSX. Un
headery unfooterson simplemente props de tipoJSX.Element, no una sintaxis especial. - Scoped slots / snippets con argumentos → hijos como función:
props.children(datos). Misma idea, misma firma. cloneElementpara inyectar props → context (comunicación por el grafo) o hijos como función. No hay traducción literal, y es buena señal.React.Children.mappara envolver →children().toArray().map(...), operando sobre nodos reales.useContextde React para estado compartido →createContextyuseContextde 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 />, omergePropspara 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.
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.
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.
- Toma un componente React con
cloneElementy reescríbelo en Solid con context. Anota qué props inyectadas pasan a ser lectura de context. - Convierte un scoped slot mental de Vue en un hijo como función de Solid.
- Implementa “named slots” con props
headeryfooterde tipoJSX.Element. - Explica en dos frases por qué
children()no es el equivalente deReact.Children, sino la respuesta a un problema que React no tiene.