Saltar al contenido

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.

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—.

Shell
# clona la plantilla TypeScript, sin historia previanpx degit solidjs/templates/ts mi-appcd mi-appnpm installnpm 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.

Shell
# asistente interactivo: Solid puro o SolidStart, TS o JSnpm 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.

Shell
# plantilla oficial de Vite para Solid con TypeScriptnpm create vite@latest mi-app -- --template solid-ts

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.

Shell
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.

HTML
<!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.

vite.config.tsTypeScript
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.

tsconfig.json (extracto)JSON
{  "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:

Shell
npm run dev      # servidor de desarrollo con recarga en calientenpm run build    # build de produccion optimizado, a la carpeta distnpm 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.

src/index.tsxTSX
/* @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.