Crear un proyecto Solid: degit, create-solid y render()
Los dos caminos canónicos para andamiar Solid en 2026 —degit sobre solidjs/templates y el asistente npm create solid—, la plantilla solid-ts de Vite, la anatomía de un proyecto mínimo, y por qué el punto de entrada render() recibe una función y no un elemento.
Solid no trae un CLI monolítico ni un runtime de build propio: se apoya en Vite y en un plugin que compila su JSX. Andamiar un proyecto es, por tanto, copiar una plantilla y dejar que Vite haga el resto. Detrás de esa sencillez hay una pieza que conviene mirar de cerca desde el minuto cero: el punto de entrada render(), donde el árbol reactivo se conecta al DOM real.
- Andamiar un proyecto con
degity con el asistentenpm create solid, sabiendo cuándo usar cada uno. - Reconocer la plantilla
solid-tsde Vite y el papel devite-plugin-solid. - Leer la anatomía de un proyecto Solid mínimo y el flujo desde
index.htmlhasta la pantalla. - Entender el punto de entrada
render()y por qué recibe una función, no un elemento ya evaluado.
Dos caminos para andamiar
El camino histórico y más ligero es degit, la utilidad de Rich Harris que clona un repositorio sin su historial de git. Las plantillas oficiales viven en el repositorio solidjs/templates, y cada subcarpeta es un punto de partida distinto —js, ts, ts-router, y variantes con Tailwind o UnoCSS—.
# clona la plantilla TypeScript, sin historia previa
npx degit solidjs/templates/ts mi-app
cd mi-app
npm install
npm run dev
El camino moderno y recomendado en 2026 es el asistente oficial, que unifica bajo un mismo comando la creación de una app Solid pura o de un proyecto SolidStart. npm create solid@latest (equivalente a npm init solid@latest) hace unas pocas preguntas —TypeScript o JavaScript, plantilla, SSR sí o no— y andamia en consecuencia.
# asistente interactivo: Solid puro o SolidStart, TS o JS
npm create solid@latest
Existe además una tercera vía perfectamente válida: la plantilla que mantiene el propio Vite. Es útil si ya piensas en clave Vite y quieres el mismo flujo que en otros frameworks.
# plantilla oficial de Vite para Solid con TypeScript
npm create vite@latest mi-app -- --template solid-ts
Si tu objetivo es entender Solid, degit solidjs/templates/ts te da el esqueleto más desnudo posible: nada que no puedas leer de una sentada. Cuando el proyecto vaya en serio —routing, SSR, server functions— arranca con npm create solid y elige SolidStart, porque migrar de una app Solid pura a SolidStart a mano es fricción evitable. Los tres caminos convergen en el mismo compilador; solo cambia cuánto andamiaje te regalan.
La anatomía de un proyecto mínimo
Lo que copia la plantilla ts es deliberadamente escaso. No hay convenciones ocultas: cada fichero está porque alguien lo puso, y puedes borrarlo sin romper ninguna maquinaria secreta.
mi-app/
├─ index.html # el host: contiene el div raiz y carga el modulo
├─ src/
│ ├─ index.tsx # punto de entrada: llama a render()
│ ├─ App.tsx # el componente raiz de tu UI
│ └─ index.css # estilos globales
├─ vite.config.ts # registra vite-plugin-solid
├─ tsconfig.json # jsx en modo preserve, jsxImportSource solid-js
└─ package.json # scripts dev, build y serve
El index.html es un documento normal: un contenedor vacío y una etiqueta script de tipo módulo que arranca la aplicación. Vite intercepta esa carga y sirve el JSX ya compilado.
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<title>Mi app Solid</title>
</head>
<body>
<div id="root"></div>
<script src="/src/index.tsx" type="module"></script>
</body>
</html>
La pieza que hace todo esto posible es vite-plugin-solid. Sin él, el navegador vería JSX crudo y fallaría; con él, Vite pasa cada .tsx por el preset de Babel de Solid, que transforma el JSX en operaciones de DOM reales antes de servirlo.
import { defineConfig } from "vite";
import solid from "vite-plugin-solid";
export default defineConfig({
plugins: [solid()],
});
Dos ficheros más completan el andamiaje y conviene abrirlos una vez. El tsconfig.json incluye la línea que hace que TypeScript entienda el JSX de Solid sin compilarlo él mismo: lo deja intacto con "jsx": "preserve" y apunta su origen a Solid con "jsxImportSource": "solid-js". Así TypeScript solo tipa; quien transforma el JSX es Babel, dentro del plugin.
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js"
}
}
El package.json aporta el vocabulario que teclearás a diario. Sus scripts son alias finos sobre Vite, no comandos propios de Solid:
npm run dev # servidor de desarrollo con recarga en caliente
npm run build # build de produccion optimizado, a la carpeta dist
npm run serve # sirve ese build para revisarlo antes de desplegar
El punto de entrada: render()
Aquí ocurre lo importante. render(), importado de solid-js/web, es la función que crea la raíz reactiva, ejecuta tu código dentro de ella e inserta el resultado en un elemento del DOM. Su firma tiene dos parámetros: el código que produce la UI y el contenedor donde montarla.
/* @refresh reload */
import { render } from "solid-js/web";
import App from "./App";
import "./index.css";
const root = document.getElementById("root");
render(() => <App />, root!);
El detalle que separa a quien entiende Solid de quien copia: el primer argumento es () => <App />, una función, no <App /> a secas. La razón es profunda. render() establece primero un contexto reactivo —un owner que gobierna efectos y limpieza— y solo entonces ejecuta el código que crea el JSX. Si le pasaras <App /> ya evaluado, el árbol se construiría fuera de esa raíz, y los efectos que naciesen ahí no tendrían dueño ni recibirían limpieza. Envolverlo en una función difiere su ejecución hasta estar dentro del contexto correcto.
El valor de retorno de render() es un dispose: al invocarlo se desmonta todo el árbol, se cancelan sus efectos y se libera el DOM insertado. En una app normal no lo usas —la aplicación vive mientras la pestaña—, pero es esencial en tests y en micro-frontends, donde montas y desmontas raíces Solid a voluntad. Que el desmontaje sea una simple llamada revela lo explícito que es el modelo de propiedad de Solid.
flowchart LR A[index.html con div root] --> B[script carga index.tsx] B --> C[render recibe callback y contenedor] C --> D[crea la raiz reactiva owner] D --> E[App se ejecuta una sola vez] E --> F[DOM real insertado en el div root]
Es tentador buscar en Solid el equivalente a un CLI todopoderoso que compile, empaquete y sirva, como si el framework tuviese su propia cadena de herramientas. No la tiene, y esa ausencia es una decisión de diseño, no una carencia. Solid es esencialmente dos cosas: una librería de reactividad de grano fino en tiempo de ejecución y un compilador de JSX que corre en tiempo de build. Todo lo demás —el servidor de desarrollo, el bundling, el HMR, el manejo de assets— lo delega en Vite, que es infraestructura compartida y madura. Por eso andamiar un proyecto se reduce a copiar unos pocos ficheros y registrar un plugin: no estás instalando un motor, estás enchufando un transformador de sintaxis a una tubería que ya existía. Interiorizar esto cambia cómo depuras y cómo razonas sobre el proyecto. Cuando algo del build falla, la pregunta correcta rara vez es “qué hace Solid raro aquí”; casi siempre es “cómo está configurado Vite” o “qué generó el plugin al compilar este JSX”. Y cuando quieras entender de verdad qué es Solid, no mires el arranque: mira la salida del compilador. Ahí, y no en un runtime oculto, vive la mitad del framework.
- Crea un proyecto con
npx degit solidjs/templates/ts mi-app, instala dependencias y arráncalo connpm run dev. - Abre
src/index.tsxy cambia() => <App />por<App />; observa el error de tipos y razona por qué Solid espera una función. - Captura el
disposeque devuelverender()en una variable, llámalo tras tres segundos consetTimeouty verifica que la UI desaparece. - Repite el andamiaje con
npm create solid@latesteligiendo SolidStart y compara qué ficheros extra aparecen frente a la plantilla dedegit.