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.
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.
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.
Traducir 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 JSXfunction 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.