wandres.dev
MODOS DE RENDER · SSR, streaming, CSR, SSG

Elegir el modo por ruta

El cierre del nivel: ningún modo de renderizado gana en abstracto, y la unidad de decisión no es la app sino la ruta. Esta lección construye un mapa de decisión sobre cuatro ejes —personalización por usuario, necesidad de SEO y primer pintado, frescura del dato e interactividad— y traduce ese mapa a arquetipos concretos: la landing y el blog al prerender, la vista pública con datos por petición al SSR en streaming, el panel tras login al CSR. Luego muestra cómo componer los modos en una sola base de código con las route rules de Nitro, para que marketing estático, app SSR y dashboard SPA convivan sin fricción, y sintetiza el nivel entero en una sola idea: renderizar es asignar coste por eje, ruta por ruta.

⏱ 16 min

Has visto cuatro formas de convertir componentes en páginas: SSR en streaming, SSR bloqueante, generación estática y render en el cliente, con islands afinando por encima. La tentación es preguntar cuál es la mejor, pero la pregunta está mal planteada: ninguna gana en abstracto, porque cada una es óptima bajo una restricción distinta. La unidad correcta de decisión no es la aplicación entera sino la ruta. Esta última lección te da un mapa para elegir modo ruta por ruta, y te muestra cómo hacer que todos convivan en una sola base de código.

🎯 Al terminar esta lección sabrás
  • Formular la decisión de render sobre cuatro ejes: personalización, SEO, frescura e interactividad.
  • Traducir esos ejes a arquetipos: marketing al prerender, vista pública al SSR, panel al SPA.
  • Componer varios modos en una sola app con las route rules de Nitro.
  • Sintetizar el nivel: renderizar es asignar coste por eje, y la ruta es la unidad de esa asignación.

Los ejes de la decisión

Antes de elegir modo, interroga a la ruta con cuatro preguntas. Sus respuestas, combinadas, casi siempre determinan el modo por sí solas.

👤

¿Cambia por usuario o petición?

Si el contenido depende de quien mira —sesion, permisos, hora— no puede congelarse en build: exige render por peticion.

🔎

¿Necesita SEO o primer pintado?

Si un buscador debe indexarla o el LCP importa, necesita HTML con contenido de servidor, no un shell vacio.

🕒

¿Cuán fresco debe ser el dato?

Inmutable va a estatico; fresco por horas a swr o isr; fresco al segundo a SSR por peticion.

🎛️

¿Cuánta interacción sostenida?

Una sesion larga y muy interactiva amortiza un arranque de cliente; una visita fugaz no lo perdona.

Ninguno de los cuatro ejes basta por sí solo; es su combinación la que decide. Una ruta con SEO pero sin personalización va a estático; con SEO y con personalización, a SSR; sin SEO y con personalización, a SPA. Los mismos valores de un eje llevan a modos opuestos según el resto, y por eso no existe una regla del tipo «si necesita SEO, entonces SSR»: existe una tabla de decisión de varias entradas, que es justo lo que el mapa de arquetipos condensa en una forma memorizable.

Un ejemplo rápido con una sola ruta, la ficha de un evento: ¿cambia por usuario? No, es pública. ¿Necesita SEO? Sí, quiere aparecer en búsquedas. ¿Frescura? El aforo cambia por horas. ¿Interactividad? Un botón de reserva y poco más. La combinación —pública, indexable, semifresca, apenas interactiva— la coloca en SSR con swr, quizás con el botón de reserva como isla. Cuatro respuestas, un veredicto, sin discutir modos en abstracto.

El primer eje es el más decisivo: la personalización por petición descarta de golpe el prerender, porque un artefacto de build no puede ver la petición. El segundo separa lo que necesita HTML de servidor —SEO, LCP— de lo que puede permitirse un shell de cliente. El tercero gradúa entre estático puro y dinámico, con swr e isr en el medio. Y el cuarto decide si el coste de arranque de un SPA se amortiza o se sufre.

Nota que los ejes no son independientes de lo que aprendiste antes: cada uno remite a un límite concreto de un modo. La personalización remite a que el prerender no ve la petición; el SEO, a que un SPA sirve un shell vacío; la frescura, a dónde cae el render en la línea del tiempo; la interactividad, a si el pago único del cliente se amortiza. El mapa de decisión no es una regla nueva que memorizar, sino el destilado de las cuatro lecciones previas en cuatro preguntas.

Por eso este cierre casi no introduce nada nuevo: pone en relación lo ya visto. Si las cuatro lecciones anteriores te enseñaron cada modo por dentro —cómo transmite el streaming, qué apaga el SPA, cuándo congela el prerender, qué acota islands—, esta te enseña a mirarlos desde fuera y a la vez, para elegir entre ellos. Es el paso de conocer las herramientas a saber cuál coger para cada tornillo.

Hay un orden natural para aplicarlos, y respetarlo ahorra vueltas. Empieza por la personalización, porque es la que más descarta: si la ruta cambia por petición, tacha el prerender y sigue. Luego el SEO y el primer pintado, que deciden si necesitas HTML de servidor o te basta un shell. Después la frescura, que gradúa entre estático, revalidado y dinámico. Y por último la interactividad, que confirma si un arranque de cliente se amortiza. Cuatro preguntas en cascada, y al final de ellas la ruta casi se ha clasificado sola.

Conviene además recordar que el modo de una ruta no es una lápida: puede evolucionar. Una vista que hoy necesita SSR porque su contenido aún se mueve puede migrar a swr cuando se estabiliza, y a prerender cuando se congela del todo. La clasificación por ejes se rehace cuando cambian las respuestas —un contenido que deja de personalizarse, un dato que empieza a exigir frescura— y el modo la sigue. Diseñar por ruta no es fijar un modo para siempre, sino instalar el hábito de revisar a qué cuadrante pertenece cada ruta cuando su naturaleza cambia.

Un mapa por arquetipo

Cruzados los ejes, emergen unos pocos arquetipos que cubren la enorme mayoría de rutas reales. Reconocer a cuál pertenece una ruta es, casi siempre, elegir su modo.

flowchart TD
A[la ruta cambia por usuario o peticion] -->|no| B[necesita frescura frecuente]
B -->|no| C[SSG prerender en build]
B -->|si| D[swr o isr con revalidacion]
A -->|si| E[necesita SEO o primer pintado]
E -->|si| F[SSR en streaming]
E -->|no| G[CSR modo SPA]
style C fill:#a6e3a1,color:#11111b
style D fill:#94e2d5,color:#11111b
style F fill:#89b4fa,color:#11111b
style G fill:#f9e2af,color:#11111b

El árbol se lee de arriba abajo con una sola pregunta por bifurcación, y su virtud es que la primera —¿cambia por usuario o petición?— parte el espacio en dos mitades limpias antes de mirar nada más. A la izquierda, lo que no cambia: territorio de build y revalidación. A la derecha, lo que sí: territorio de servidor y cliente. Solo entonces entran en juego el SEO y la frescura para afinar la hoja concreta.

Las rutas que parecen no encajar en ninguna hoja suelen ser rutas mal delimitadas. Si una misma URL mezcla contenido público indexable y un panel personal, probablemente son dos rutas disfrazadas de una, y separarlas —una estática, otra dinámica— disuelve el aparente conflicto de modo. El mapa no solo elige el modo: a veces revela que tu ruta estaba haciendo dos trabajos que pedían modos distintos.

Marketing y contenido —landing, precios, blog, documentación—: no cambian por usuario y viven de indexarse. Van a prerender, con islands por encima si tienen algún widget suelto. Es el cuadrante de latencia mínima y coste nulo por visita. Es también donde más se peca por exceso de sofisticación: una landing no necesita SSR, ni hidratación completa, ni un servidor vivo, sino HTML rápido y bien indexado —justo lo que el prerender entrega con el menor aparato posible—. Cuando veas una página de marketing servida por un render dinámico, sospecha; casi siempre es complejidad que no compra nada.

Vista pública con datos por petición —una ficha de producto con stock, un perfil público, resultados de búsqueda—: cambian y necesitan SEO. Van a SSR en streaming, que da HTML indexable y primer pintado temprano sin atarse al dato más lento; si el dato tolera algo de retraso, swr o isr recortan el cómputo. Este arquetipo es el que más se beneficia de todo el primer tema: el streaming le da el HTML que el buscador necesita sin sacrificar el primer pintado, y deferStream le garantiza que el título y la imagen principal viajen en el shell. No por casualidad es el caso para el que SolidStart optimizó su modo por defecto.

Fíjate en que este arquetipo admite además un ajuste fino por dato: el precio, que debe ser fresco, puede ir por createAsync sin cachear, mientras la descripción, estable, se sirve del estático o de la revalidación lenta. Dentro de una misma ruta SSR conviven datos con distinta política de frescura, y afinarlos uno a uno es la última vuelta de tuerca sobre el modo base que elegiste para la ruta.

App tras login —panel, editor, back-office—: cambian por usuario pero nadie las indexa. Son el hogar del CSR / SPA, donde el arranque único se amortiza en una sesión larga y el servidor se reduce a repartir estáticos. Aquí el error simétrico es el exceso de servidor: montar SSR para un panel privado añade un proceso de render por petición, un estado que aislar y una latencia por navegación, todo para proteger un primer pintado que el usuario autenticado ni siquiera nota. Cada arquetipo tiene su tentación, y en los dos extremos —marketing y app privada— es la misma: usar más máquina de la que la ruta pide.

Un cuarto arquetipo merece mención por lo frecuente: el contenido casi estático que envejece despacio —un catálogo, una portada de noticias, precios que cambian a diario—. No encaja del todo ni en marketing congelado ni en SSR por petición: va a swr o isr, sirviendo un estático la mayoría de las veces y revalidando en segundo plano. Reconocer que existe evita el error de forzar estas rutas a uno de los dos extremos y pagar de más en frescura o en cómputo.

Sobre estos arquetipos, islands es una capa opcional, no un quinto arquetipo. Se superpone al de marketing y contenido: una landing prerenderizada con un solo widget interactivo es la candidata perfecta a hidratar solo ese widget. En los otros dos —la vista pública con datos y la app tras login— islands rara vez compensa, porque su interactividad no es una isla suelta sino el tejido de la página. Piensa en islands como un adjetivo que a veces acompaña al modo estático, no como un modo que compita con los demás.

💡
El arquetipo dominante decide; los matices afinan

No busques el modo perfecto para cada píxel: identifica primero a qué arquetipo pertenece la ruta —marketing, vista pública, app privada— y adopta su modo por defecto. Luego afina con las perillas finas: un deferStream para el LCP de una ficha, un clientOnly para un mapa, un swr para un dato que envejece despacio. El arquetipo fija el 90 % de la decisión; los matices, el resto.

Componer los modos en una sola app

Lo mejor de SolidStart es que estos arquetipos no obligan a partir el proyecto: conviven en una sola base de código, cada ruta con su modo, gracias a las route rules de Nitro. Una misma app sirve marketing congelado, vistas públicas en streaming y un panel como SPA.

Esto no siempre fue posible, y conviene apreciarlo. Durante años, mezclar un sitio estático de marketing con una app dinámica obligaba a dos proyectos, dos despliegues y a menudo dos tecnologías, cosidos por un proxy. Que un solo framework entregue los cuatro modos desde una configuración de rutas es una madurez reciente del ecosistema, y es lo que convierte «elegir el modo por ruta» de aspiración en práctica cotidiana.

// app.config.ts
import { defineConfig } from "@solidjs/start/config";

export default defineConfig({
  server: {
    routeRules: {
      "/": { prerender: true },          // landing estatica
      "/blog/**": { prerender: true },   // contenido estatico
      "/producto/**": { swr: 600 },      // publico, revalida cada 10 min
      "/buscar": { ssr: true },          // dinamico por peticion, con SEO
      "/app/**": { ssr: false },         // panel tras login: SPA
    },
  },
});

Cada patrón declara la política de su familia de rutas, y Nitro la aplica en el build y en el servidor. El visitante que llega a la landing recibe un estático desde el CDN; el que abre una ficha, un estático revalidado; el que busca, HTML renderizado al momento; y el que entra al panel, un shell que hidrata su app. Un solo despliegue, un solo repositorio, cada ruta en su punto óptimo del espectro. Léelo despacio, porque es la síntesis operativa del nivel: cuatro visitantes, cuatro rutas, cuatro modos, un solo proyecto. Nada de esto exigió elegir un bando; exigió mirar cada ruta y darle lo que pedía.

Lo valioso de esto no es solo técnico sino organizativo: un único repositorio, un único pipeline de despliegue, un único modelo mental de componentes, y sin embargo cada ruta entregada de la forma que su naturaleza pide. No hay que partir la app en un sitio estático y otro dinámico, ni mantener dos bases de código; el modo es una propiedad declarada de la ruta, no una decisión que se pague en fragmentación. Esa es la promesa que cierra el nivel: la sofisticación del reparto de costes sin la penalización de la complejidad estructural.

Merece notar que esta composición es posible porque el modo se declara fuera de los componentes, en la configuración de rutas, y no dentro de ellos. Tu ListaProductos no sabe si se prerenderiza, se transmite o se hidrata en un SPA; esa decisión vive en las route rules, un plano aparte. Separar el qué —los componentes— del cómo se entregan —el modo— es lo que evita que mezclar modos degenere en mezclar código, y es la razón última de que una sola base sirva los cuatro cuadrantes sin fricción.

Anti-patrones: elegir mal el modo

Los errores de modo son casi siempre el mismo error: aplicar a una ruta el modo que domina en otra parte de la app, por inercia en vez de por análisis. Tres se repiten con obstinación.

El primero es servir una landing pública como SPA. La página que más depende de su LCP y de indexarse acaba entregando un shell vacío al visitante frío y al rastreador; el coste se paga en el peor momento posible, el del primer contacto. El segundo es prerenderizar contenido personalizado: congelar en build una vista que lee la sesión produce el mismo estado para todos, porque en el build no hay usuario. El tercero, más sutil, es poner SSR por petición en todo por defecto, incluido lo que no cambia nunca, pagando cómputo por ruta en páginas que un CDN serviría gratis.

Los tres comparten una raíz psicológica: elegir el modo por lo que resulta familiar al equipo, no por lo que la ruta necesita. Un equipo cómodo con SPAs hará SPAs de más; uno enamorado del SSR lo pondrá donde estorba; uno que descubrió el prerender lo forzará sobre datos que cambian. El modo correcto a veces es, precisamente, el que menos te apetece mantener.

El antídoto es común a los tres: no heredar el modo, decidirlo. Un ejemplo fija el hábito. Ante una tienda, no preguntes «¿qué usa mi app?»; pregunta por cada ruta: la portada no cambia por usuario y vive de Google, luego prerender; la ficha de producto cambia despacio y necesita SEO, luego swr; el carrito es personal y efímero, luego cliente; el checkout es personal y sensible, luego SSR por petición. Cuatro rutas de una misma tienda, cuatro modos distintos, y ninguna contradicción: cada una respondió por su cuenta a los cuatro ejes.

Detrás de los tres anti-patrones late una misma causa: tratar el modo como una propiedad de la aplicación —«somos una app SSR», «somos un SPA»— en vez de una propiedad de la ruta. Esa etiqueta global es cómoda para presentarse en una charla, pero es precisamente la que produce landings sin indexar y dashboards con SSR innecesario. La madurez empieza cuando dejas de responder «¿qué somos?» y empiezas a responder, ruta por ruta, «¿qué necesita esta?».

Ese cambio de pregunta es, en el fondo, todo lo que este nivel quería enseñar. Los modos de renderizado no son bandos a los que uno se afilia, sino herramientas entre las que se elige, y la unidad de elección es siempre la ruta. Quien lo interioriza deja de discutir cuál es el mejor modo y empieza a repartir cada ruta al suyo —que es donde vive de verdad el rendimiento de una aplicación web—.

Renderizar es asignar coste por eje, y la ruta es la unidad de esa asignación

Si este nivel deja un solo poso, que sea este: no existe «el modo de renderizado de tu app», existe el modo de cada ruta, y elegirlo es un acto de ingeniería económica, no de moda. Cada modo que has estudiado mueve el coste inevitable de construir una interfaz a lo largo de un eje distinto. El SSG lo adelanta en el tiempo, pagándolo una vez en el build a cambio de no poder ver la petición. El SSR en streaming lo solapa en el tiempo de entrega, dejando que el navegador trabaje mientras el servidor aún resuelve, a cambio de un servidor vivo por petición. El CSR lo traslada en el lugar, mudándolo entero al cliente a cambio de un primer pintado tardío y un SEO frágil. E islands lo acota en el espacio, limitando qué regiones merecen volverse interactivas. Ninguno elimina el coste —renderizar siempre cuesta lo que cuesta—; todos lo reubican, y cada reubicación es acertada bajo unas restricciones y ruinosa bajo otras. Por eso la pregunta madura nunca es «¿SSR o SPA?, ¿estático o dinámico?», sino «¿qué restricción domina en esta ruta —indexación, frescura, interactividad, personalización— y qué eje de coste puedo permitirme cargar para respetarla?». Una landing tolera pagar en el build porque no cambia; un panel tolera pagar en el arranque porque la sesión es larga; una ficha pública no tolera ni un shell vacío ni un dato rancio, y por eso vive en el streaming con revalidación. Cuando ves los modos así —no como recetas rivales sino como diales de una misma consola de costes, uno por eje, todos combinables ruta por ruta— dejas de preguntarte cuál es el mejor y empiezas a diseñar el reparto: y esa es, entera, la competencia que separa a quien usa un framework de quien arquitecta con él.

⚔️ Dibuja el mapa de render de una app real
  1. Toma una app con al menos cinco tipos de ruta —landing, blog, ficha pública, búsqueda, panel privado— y clasifícalas con los cuatro ejes: personalización, SEO, frescura, interactividad.
  2. Asigna a cada una su arquetipo y su modo siguiendo el mapa de decisión; justifica cada asignación por el eje que domina.
  3. Escribe las route rules de Nitro que las hagan convivir: prerender, swr, ssr y ssr: false según corresponda.
  4. Elige una ruta y afínala con una perilla fina —deferStream, clientOnly o isr— y argumenta qué métrica mejora.
  5. Redacta en un párrafo, para tu app, por qué renderizar es asignar coste por eje y por qué la ruta —no la aplicación— es la unidad correcta de esa decisión.