wandres.dev
SOLIDSTART · el meta-framework

Crear un proyecto SolidStart y su estructura

Cómo se arranca un proyecto SolidStart con el CLI create-solid y qué esqueleto genera. Los cuatro pilares del directorio src: app.tsx como raíz compartida entre cliente y servidor donde vive el Router y FileRoutes, entry-client como arranque del navegador, entry-server como definición del documento HTML y el handler de servidor, y la carpeta routes donde cada fichero se convierte en una ruta. Qué papel cumple cada pieza, cómo se relacionan entre sí y por qué esta disposición mínima ya es una aplicación isomorfa completa.

⏱ 16 min

Un proyecto SolidStart recién creado tiene menos ficheros de los que esperarías, y esa parquedad es intencionada: casi todo el trabajo pesado lo hacen las capas de abajo, así que lo que queda en tu directorio son justo las piezas que te pertenecen a ti. Son cuatro los pilares que importan —el fichero raíz app.tsx, los dos entry points entry-client y entry-server, y la carpeta routes— y entender la función de cada uno, más cómo se enlazan, es tener el mapa completo de por dónde entra y sale la ejecución de tu aplicación. Esta lección arranca el proyecto y recorre ese esqueleto hasta que deje de parecer una caja negra y pase a ser un circuito que sabes leer.

🎯 Al terminar esta lección sabrás
  • Arrancar un proyecto con create-solid y entender qué elige el asistente por ti.
  • Reconocer los cuatro pilares de src: app.tsx, entry-client, entry-server y routes.
  • Explicar por qué app.tsx es la raíz compartida y qué monta con Router y FileRoutes.
  • Ver cómo la carpeta routes se traduce en un árbol de rutas por convención.

Arrancar el proyecto

El punto de entrada es el CLI oficial, que se invoca con npm init o su equivalente en otros gestores.

npm init solid@latest
# o con otros gestores
pnpm create solid@latest
bun create solid@latest

El asistente hace pocas preguntas, pero cada una fija una decisión de arquitectura. Te pide el nombre del proyecto; una plantilla —desde bare hasta variantes con Tailwind o autenticación—; si quieres Server Side Rendering (responder que no arranca el proyecto en modo SPA, que verás en la última lección); y si usas TypeScript. Con esas respuestas escribe el esqueleto y te deja instalar dependencias y levantar el servidor.

cd mi-app
npm install
npm run dev

Recuerda de la lección anterior que ese dev invoca a Vinxi por debajo. El asistente no ha instalado un framework monolítico: ha compuesto una configuración de Vinxi con las convenciones de Solid, y lo que ha dejado en tu carpeta es la parte editable de esa composición.

El esqueleto: los cuatro pilares

Una plantilla bare con TypeScript deja un árbol muy corto. Estos son los ficheros que gobiernan la ejecución:

mi-app/
├── public/            # activos servidos tal cual (favicon, imagenes)
├── src/
   ├── routes/        # cada fichero es una ruta
   └── index.tsx  # la ruta /
   ├── app.tsx        # raiz compartida: Router y FileRoutes
   ├── entry-client.tsx  # arranque en el navegador
   ├── entry-server.tsx  # documento HTML y handler de servidor
   └── global.d.ts    # tipos del entorno de SolidStart
├── app.config.ts      # configuracion del proyecto (siguiente leccion)
├── package.json
└── tsconfig.json
flowchart TD
R[carpeta routes ficheros igual a rutas] --> A[app.tsx raiz con Router y FileRoutes]
A --> EC[entry-client hidrata en el navegador]
A --> ES[entry-server renderiza y envuelve en el documento HTML]
style A fill:#89b4fa,color:#11111b
style EC fill:#a6e3a1,color:#11111b
style ES fill:#f9e2af,color:#11111b

La lectura del diagrama es la clave del nivel: app.tsx es el centro, la raíz compartida que ambos entornos ejecutan. entry-server la renderiza a HTML en el servidor; entry-client la hidrata en el navegador; y routes es el material que app.tsx monta dentro. Los dos entry points son puertas de entrada específicas de cada entorno; el árbol de aplicación es uno solo.

app.tsx: la raíz compartida

app.tsx exporta el componente raíz de tu aplicación, el que se ejecuta idéntico en servidor y cliente. Su trabajo canónico es montar el enrutador y declarar el envoltorio común de todas las rutas —proveedores de contexto, un layout global, la frontera de Suspense—.

// src/app.tsx
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { Suspense } from "solid-js";

export default function App() {
  return (
    <Router
      root={(props) => (
        <Suspense>{props.children}</Suspense>
      )}
    >
      <FileRoutes />
    </Router>
  );
}

Dos piezas hacen el trabajo. Router, de @solidjs/router, es el enrutador cuya prop root define el marco que envuelve todas las rutas: aquí es donde colocas los proveedores que deben existir en cada página, tal como viste al aislar estado por petición en el nivel de SSR. FileRoutes, de @solidjs/start/router, es la magia de SolidStart: en tiempo de build lee tu carpeta routes y genera el árbol de rutas, que inserta como hijos del Router. No escribes una tabla de rutas: la declaras con la estructura de ficheros y FileRoutes la materializa.

ℹ️
La raiz es el sitio de los proveedores globales

Todo proveedor de contexto que deba estar disponible en cualquier ruta vive en la prop root del Router, no en los entry points. Es el lugar correcto porque root se ejecuta una vez por petición en el servidor y una vez por pestaña en el cliente, exactamente el ciclo de vida que necesita un estado por usuario. Tentarse a poner proveedores en entry-server o entry-client es un error clásico: esos ficheros son arranque de entorno, no el árbol de tu aplicación.

La carpeta routes: enrutado por ficheros

La convención central de SolidStart es que la forma de src/routes es el mapa de URLs. Un fichero se convierte en una ruta cuyo componente por defecto se renderiza en esa URL. No hay registro que mantener ni import que recordar: creas el fichero y la ruta existe.

// src/routes/index.tsx  ->  la ruta /
export default function Home() {
  return <h1>Hola SolidStart</h1>;
}
// src/routes/sobre.tsx  ->  la ruta /sobre
export default function Sobre() {
  return <p>Una pagina servida por convencion de ficheros.</p>;
}

El mapeo sigue reglas legibles a simple vista: index.tsx es la raíz de su carpeta, un nombre de fichero es un segmento literal de la URL, y una subcarpeta anida un tramo de la ruta. Así, una carpeta traduce su jerarquía directamente a la de las URLs:

src/routes/
├── index.tsx          # /
├── sobre.tsx          # /sobre
└── blog/
    ├── index.tsx      # /blog
    └── novedades.tsx  # /blog/novedades

Las piezas más ricas del enrutado —parámetros dinámicos, rutas anidadas con layouts compartidos, grupos— pertenecen a su propio nivel más adelante; lo que importa fijar hoy es el principio: la estructura del sistema de ficheros es la fuente de verdad del enrutado, y FileRoutes es quien la lee en el build para tejer el árbol que el Router consume. Cambiar la navegación de tu producto es, literalmente, mover ficheros de sitio.

⚠️
Los entry points casi nunca se tocan; app.tsx sí

Un error de principiante es tratar de configurar la aplicación editando entry-client o entry-server. En el flujo normal esos ficheros se quedan como vienen: son arranque de entorno. El lugar donde de verdad construyes la aplicación —enrutador, proveedores, layout, límites de carga y error— es app.tsx y la carpeta routes. Reserva los entry points para lo que de verdad pertenece al arranque de un entorno concreto, que es justo el tema de la lección cuatro; para todo lo demás, tu mano vive en la raíz y en las rutas.

Un centro compartido y dos puertas: esa asimetria es todo el modelo mental

La disposición de un proyecto SolidStart codifica, en forma de ficheros, la idea más importante del framework: que hay un solo árbol de aplicación y dos entornos que lo ejecutan. app.tsx es ese árbol, el centro compartido, y por eso es el único fichero donde tiene sentido montar el enrutador, colgar los proveedores y decidir el layout: lo que escribas ahí correrá idéntico cuando el servidor produzca el HTML de la primera carga y cuando el navegador tome el relevo para hacerlo interactivo. A su alrededor, los dos entry points son las puertas por las que cada entorno entra a ese árbol, y su asimetría no es casual: entry-server tiene que fabricar el documento HTML completo porque en el servidor no existe todavía ninguna página, mientras que entry-client solo tiene que engancharse a un documento que ya llegó, hidratándolo. Comprender esa asimetría —uno crea la página desde la nada, el otro reanima una que ya está— disuelve casi todas las confusiones sobre dónde va cada cosa: la configuración del proyecto va en app.config.ts; el árbol de la aplicación, en app.tsx y routes; y el arranque de cada entorno, en su entry point, que tocarás en contadas ocasiones. La carpeta routes cierra el modelo aportando la última pieza: que el mapa de la aplicación no se declara en código imperativo sino en la forma del sistema de ficheros, de modo que la arquitectura de navegación de tu producto es literalmente legible con un ls. Cuando esta geografía se te vuelve evidente, dejas de preguntarte en qué fichero va una cosa y empiezas a saberlo por su naturaleza: si pertenece al árbol, si configura el proyecto o si arranca un entorno. Ese criterio, y no la memoria de rutas de ficheros, es lo que convierte el esqueleto en un mapa que lees sin esfuerzo.

⚔️ Recorre y modifica el esqueleto
  1. Crea un proyecto con create-solid, elige la plantilla bare con SSR y TypeScript, y levántalo con npm run dev.
  2. Localiza los cuatro pilares en src y escribe en una frase la responsabilidad de cada uno sin mirar la lección.
  3. Añade src/routes/sobre.tsx con un componente por defecto y comprueba que la URL /sobre la sirve sin que registres nada.
  4. Abre app.tsx y explica qué inserta FileRoutes como hijo del Router y de dónde saca ese contenido.
  5. Coloca un proveedor de contexto trivial en la prop root del Router y argumenta por qué ese es su sitio y no un entry point.