Renderización en la Web

Publicado el 6 de febrero de 2019. Última actualización el 5 de enero de 2026

Una de las decisiones principales que deben tomar los desarrolladores web es dónde implementar la lógica y la renderización en su aplicación. Esto puede ser difícil porque hay muchas formas de compilar un sitio web.

Nuestra comprensión de este espacio se basa en nuestro trabajo en Chrome, en el que hablamos con sitios grandes en los últimos años. En términos generales, recomendamos a los desarrolladores que consideren la renderización del servidor o la renderización estática en lugar de un enfoque de rehidratación completa.

Para comprender mejor las arquitecturas entre las que elegimos cuando tomamos esta decisión, necesitamos una terminología coherente y un framework compartido para cada enfoque. Luego, puedes evaluar mejor las compensaciones de cada enfoque de renderización desde la perspectiva del rendimiento de la página.

Terminología

Primero, definimos algunos términos que usaremos.

Renderización

Renderización del servidor (SSR)
Renderizar una app en el servidor para enviar HTML, en lugar de JavaScript, al cliente.
Renderización del cliente (CSR)
Renderizar una app en un navegador con JavaScript para modificar el DOM.
Renderización previa
Ejecutar una aplicación del cliente en el momento de la compilación para capturar su estado inicial como HTML estático. Ten en cuenta que la "renderización previa" en este sentido es diferente de la renderización previa del navegador de navegaciones futuras.
Hidratación
Ejecutar secuencias de comandos del cliente para agregar estado de la aplicación e interactividad al HTML renderizado por el servidor. La hidratación supone que el DOM no cambia.
Rehidratación
Si bien a menudo se usa para significar lo mismo que la hidratación, la rehidratación implica actualizar el DOM con regularidad con el estado más reciente, incluso después de la hidratación inicial

Rendimiento

Time to First Byte (TTFB)
Es el tiempo que transcurre entre hacer clic en un vínculo y la carga del primer byte de contenido en la página nueva.
First Contentful Paint (FCP)
Es el momento en que el contenido solicitado (cuerpo del artículo, etcétera) se vuelve visible.
Interaction to Next Paint (INP)
Es una métrica representativa que evalúa si una página responde de forma coherente y rápida a las entradas del usuario.
Total Blocking Time (TBT)
Es una métrica proxy para INP que calcula cuánto tiempo se bloqueó el subproceso principal durante la carga de la página.

Renderización del servidor

La renderización del servidor genera el HTML completo de una página en el servidor en respuesta a la navegación. Esto evita viajes de ida y vuelta adicionales para la recuperación de datos y la creación de plantillas en el cliente, ya que el procesador los controla antes de que el navegador obtenga una respuesta.

La renderización del servidor generalmente produce un FCP rápido. Ejecutar la lógica de la página y la renderización en el servidor te permite evitar enviar mucho JavaScript al cliente. Esto ayuda a reducir el TTBT de una página, lo que también puede generar un INP más bajo, ya que el subproceso principal no se bloquea con tanta frecuencia durante la carga de la página. Cuando el subproceso principal se bloquea con menos frecuencia, las interacciones del usuario tienen más oportunidades de ejecutarse antes.

Esto tiene sentido, ya que, con la renderización del servidor, solo envías texto y vínculos al navegador del usuario. Este enfoque puede funcionar bien para una variedad de condiciones de red y dispositivos, y abre interesantes optimizaciones del navegador, como el análisis de documentos de transmisión.

Diagrama en el que se muestra la renderización del servidor y la ejecución de JavaScript que afectan el FCP y el TTI.
FCP y TTI con renderización del servidor.

Con la renderización del servidor, es menos probable que los usuarios tengan que esperar a que se ejecute JavaScript vinculado a la CPU antes de poder usar tu sitio. Incluso cuando no puedes evitar JavaScript de terceros, usar la renderización del servidor para reducir tus propios costos de JavaScript de origen puede darte más presupuesto para el resto. Sin embargo, existe una posible compensación con este enfoque: la generación de páginas en el servidor lleva tiempo, lo que puede aumentar el TTFB de tu página.

Si la renderización del servidor es suficiente para tu aplicación, depende en gran medida de qué tipo de experiencia estés creando. Existe un debate de larga data sobre las aplicaciones correctas de la renderización del servidor en comparación con la renderización del cliente, pero siempre puedes elegir usar la renderización del servidor para algunas páginas y no para otras. Algunos sitios adoptaron técnicas de renderización híbrida con éxito. Por ejemplo, Netflix renderiza en el servidor sus páginas de destino relativamente estáticas, mientras que prefetching de JavaScript para las páginas con muchas interacciones, lo que les da a estas páginas renderizadas por el cliente más pesadas una mejor oportunidad de cargarse rápidamente.

Con muchos frameworks, bibliotecas y arquitecturas modernos, puedes renderizar la misma aplicación en el cliente y en el servidor. Puedes usar estas técnicas para la renderización del servidor. Sin embargo, las arquitecturas en las que la renderización se realiza tanto en el servidor y en el cliente son su propia clase de solución con características de rendimiento y compensaciones muy diferentes. Los usuarios de React pueden usar las APIs de DOM del servidor o las soluciones basadas en ellas, como Next.js, para la renderización del servidor. Los usuarios de Vue pueden usar la guía de renderización del servidor de Vue o Nuxt. Angular tiene Universal.

Sin embargo, la mayoría de las soluciones populares usan alguna forma de hidratación, por lo que debes tener en cuenta los enfoques que usa tu herramienta.

Renderización estática

La renderización estática se realiza en el momento de la compilación. Este enfoque ofrece un FCP rápido y también un TBT y INP más bajos, siempre que limites la cantidad de JavaScript del cliente en tus páginas. A diferencia de la renderización del servidor, también logra un TTFB rápido y coherente, ya que el HTML de una página no tiene que generarse de forma dinámica en el servidor. Por lo general, la renderización estática significa producir un archivo HTML separado para cada URL con anticipación. Con las respuestas HTML generadas con anticipación, puedes implementar renderizaciones estáticas en varias CDN para aprovechar el almacenamiento en caché perimetral.

Diagrama que muestra la renderización estática y la ejecución opcional de JavaScript que afectan el FCP y el TTI.
FCP y TTI con renderización estática.

Las soluciones para la renderización estática vienen en todas las formas y tamaños. Las herramientas como Gatsby están diseñadas para que los desarrolladores sientan que su aplicación se renderiza de forma dinámica, no se genera como un paso de compilación. Las herramientas de generación de sitios estáticos, como 11ty, Jekyll, y Metalsmith adoptan su naturaleza estática y proporcionan un enfoque más basado en plantillas.

Una de las desventajas de la renderización estática es que debe generar archivos HTML individuales para cada URL posible. Esto puede ser difícil o incluso inviable cuando necesitas predecir esas URLs con anticipación y para sitios con una gran cantidad de páginas únicas.

Es posible que los usuarios de React estén familiarizados con Gatsby, la exportación estática de Next.js o Navi, que facilitan la creación de páginas a partir de componentes. Sin embargo, la renderización estática y la renderización previa se comportan de manera diferente: las páginas renderizadas de forma estática son interactivas sin necesidad de ejecutar mucho JavaScript del cliente, mientras que la renderización previa mejora el FCP de una aplicación de una sola página que se debe iniciar en el cliente para que las páginas sean verdaderamente interactivas.

Si no estás seguro de si una solución determinada es renderización estática o renderización previa, inhabilita JavaScript y carga la página que deseas probar. En el caso de las páginas renderizadas de forma estática, la mayoría de las funciones interactivas aún existen sin JavaScript. Es posible que las páginas renderizadas previamente aún tengan algunas funciones básicas, como vínculos con JavaScript inhabilitado, pero la mayor parte de la página es inerte.

Otra prueba útil es usar la limitación de red en las Herramientas para desarrolladores de Chrome y ver cuánto JavaScript se descarga antes de que una página se vuelva interactiva. Por lo general, la renderización previa necesita más JavaScript para volverse interactiva, y ese JavaScript tiende a ser más complejo que el enfoque de mejora progresiva que se usa en la renderización estática.

Renderización del servidor versus renderización estática

La renderización del servidor no es la mejor solución para todo, ya que su naturaleza dinámica puede tener costos significativos de sobrecarga de procesamiento. Muchas soluciones de renderización del servidor no vacían antes, retrasan el TTFB o duplican los datos que se envían (por ejemplo, los estados intercalados que usa JavaScript en el cliente). En React, renderToString() puede ser lento porque es síncrono y de un solo subproceso. Las APIs de DOM del servidor de React más recientes admiten la transmisión, lo que puede hacer que la parte inicial de una respuesta HTML llegue al navegador antes mientras el resto aún se genera en el servidor.

Para que la renderización del servidor sea "correcta", se puede encontrar o compilar una solución para el almacenamiento en caché de componentes, administrar el consumo de memoria, usar técnicas de memoización, y otras consideraciones. A menudo, procesas o vuelves a compilar la misma app dos veces, una en el cliente y otra en el servidor. La renderización del servidor que muestra contenido antes no necesariamente te da menos trabajo. Si tienes mucho trabajo en el cliente después de que llega una respuesta HTML generada por el servidor, esto puede generar un TBT y un INP más altos para tu sitio web.

La renderización del servidor produce HTML a pedido para cada URL, pero puede ser más lenta que solo entregar contenido renderizado estático. Si puedes realizar el trabajo adicional la renderización del servidor más el almacenamiento en caché de HTML pueden reducir significativamente el tiempo de renderización del servidor. La ventaja de la renderización del servidor es la capacidad de extraer más datos "en vivo" y responder a un conjunto más completo de solicitudes que es posible con la renderización estática. Las páginas que necesitan personalización son un ejemplo concreto del tipo de solicitud que no funciona bien con la renderización estática.

La renderización del servidor también puede presentar decisiones interesantes cuando se compila una PWA. ¿Es mejor usar el almacenamiento en caché del service worker de página completa o renderizar en el servidor fragmentos de contenido individuales?

Renderización del cliente

La renderización del cliente significa renderizar páginas directamente en el navegador con JavaScript. Toda la lógica, la recuperación de datos, la creación de plantillas y el enrutamiento se controlan en el cliente en lugar de en el servidor. El resultado efectivo es que se pasan más datos al dispositivo del usuario desde el servidor, y eso conlleva su propio conjunto de compensaciones.

La renderización del cliente puede ser difícil de hacer y mantener rápida para dispositivos móviles. Con un poco de trabajo para mantener un presupuesto ajustado de JavaScript y entregar valor en la menor cantidad de viajes de ida y vuelta posible, puedes hacer que la renderización del cliente casi replique el rendimiento de la renderización del servidor pura. Puedes hacer que el analizador funcione más rápido si entregas secuencias de comandos y datos críticos con <link rel=preload> También te recomendamos que consideres usar patrones como PRPL para garantizar que las navegaciones iniciales y posteriores se sientan instantáneas.

Diagrama que muestra la renderización del cliente que afecta el FCP y el TTI.
FCP y TTI con renderización del cliente.

La principal desventaja de la renderización del cliente es que la cantidad de JavaScript requerida tiende a aumentar a medida que crece una aplicación, lo que puede afectar el INP de una página. Esto se vuelve especialmente difícil con la adición de nuevas bibliotecas de JavaScript, polyfills y código de terceros, que compiten por la potencia de procesamiento y, a menudo, deben procesarse antes de que se pueda renderizar el contenido de una página.

Las experiencias que usan la renderización del cliente y dependen de paquetes grandes de JavaScript deben considerar la división agresiva del código para reducir el TBT y el INP durante la carga de la página, así como la carga diferida de JavaScript para entregar solo lo que el usuario necesita, cuando lo necesita. Para las experiencias con poca o ninguna interactividad, la renderización del servidor puede representar una solución más escalable para estos problemas.

Para las personas que compilan aplicaciones de una sola página, identificar las partes principales de la interfaz de usuario que comparten la mayoría de las páginas te permite aplicar la técnica de almacenamiento en caché de la shell de la aplicación. En combinación con los service workers, esto puede mejorar drásticamente el rendimiento percibido en las visitas repetidas, ya que la página puede cargar su HTML de shell de la aplicación y las dependencias de CacheStorage muy rápido.

La rehidratación combina la renderización del servidor y del cliente

La hidratación es un enfoque que mitiga las compensaciones entre la renderización del cliente y del servidor, ya que realiza ambas. Las solicitudes de navegación, como las cargas o las recargas de página completa, se controlan mediante un servidor que renderiza la aplicación en HTML. Luego, el JavaScript y los datos que se usan para la renderización se incorporan al documento resultante. Cuando se realiza con cuidado, esto logra un FCP rápido como la renderización del servidor y, luego, "se recupera" volviendo a renderizar en el cliente.

Esta es una solución eficaz, pero puede tener desventajas de rendimiento considerables.

La principal desventaja de la renderización del servidor con rehidratación es que puede tener un impacto negativo significativo en el TBT y el INP, incluso si mejora el FCP. Las páginas renderizadas por el servidor pueden parecer cargadas e interactivas, pero no pueden responder a la entrada hasta que se ejecuten las secuencias de comandos del cliente para los componentes y se hayan adjuntado los controladores de eventos. En dispositivos móviles, esto puede llevar minutos, lo que confunde y frustra al usuario.

Un problema de rehidratación: una app por el precio de dos

Para que JavaScript del cliente se haga cargo con precisión de donde lo dejó el servidor, sin volver a solicitar todos los datos con los que el servidor renderizó su HTML, la mayoría de las soluciones de renderización del servidor serializan la respuesta de las dependencias de datos de una IU como etiquetas de secuencia de comandos en el documento. Debido a que esto duplica mucho HTML, la rehidratación puede causar más problemas que solo la interactividad retrasada.

Documento HTML que contiene una IU serializada, datos intercalados y una secuencia de comandos bundle.js.

El servidor muestra una descripción de la IU de la aplicación en respuesta a una solicitud de navegación, pero también muestra los datos de origen que se usan para componer esa IU y una copia completa de la implementación de la IU que luego se inicia en el cliente. La IU no se vuelve interactiva hasta que bundle.js termina de cargarse y ejecutarse.

Las métricas de rendimiento recopiladas de sitios web reales que usan la renderización del servidor y la rehidratación indican que rara vez es la mejor opción. El motivo más importante es su efecto en la experiencia del usuario, cuando una página parece lista, pero ninguna de sus funciones interactivas funciona.

Los efectos negativos de la renderización del cliente en el TTI.

Hay esperanzas para la renderización del servidor con rehidratación. A corto plazo, solo usar la renderización del servidor para contenido altamente almacenable en caché puede reducir el TTFB, lo que produce resultados similares a la renderización previa. La rehidratación incremental, progresiva o parcial podría ser la clave para que esta técnica sea más viable en el futuro.

Transmite la renderización del servidor y rehidrata de forma progresiva

La renderización del servidor tuvo varios desarrollos en los últimos años.

La renderización del servidor de transmisión te permite enviar HTML en fragmentos que el navegador puede renderizar de forma progresiva a medida que se recibe. Esto puede hacer que el lenguaje de marcado llegue a tus usuarios más rápido, lo que acelera tu FCP. En React, las transmisiones asíncronas en renderToPipeableStream(), en comparación con síncrono renderToString(), significa que la contrapresión se controla bien.

La rehidratación progresiva también vale la pena considerar (React la implementó). Con este enfoque, las partes individuales de una aplicación renderizada por el servidor se "inician" con el tiempo, en lugar del enfoque común actual de inicializar toda la aplicación a la vez. Esto puede ayudar a reducir la cantidad de JavaScript necesario para que las páginas sean interactivas, ya que te permite diferir la actualización del cliente de las partes de baja prioridad de la página para evitar que bloquee el subproceso principal, lo que permite que las interacciones del usuario se produzcan antes después de que el usuario las inicie.

La rehidratación progresiva también puede ayudarte a evitar uno de los errores de rehidratación de renderización del servidor más comunes: se destruye un árbol DOM renderizado por el servidor y, luego, se vuelve a compilar de inmediato, por lo general, porque la renderización inicial síncrona del cliente requería datos que no estaban listos, a menudo, un Promise que aún no se resolvió.

Rehidratación parcial

La rehidratación parcial demostró ser difícil de implementar. Este enfoque es una extensión de la rehidratación progresiva que analiza partes individuales de la página (componentes, vistas o árboles) y, luego, identifica las partes con poca interactividad o sin reactividad. Para cada una de estas partes mayormente estáticas, el código JavaScript correspondiente se transforma en referencias inertes y funciones decorativas, lo que reduce su presencia del cliente a casi cero.

El enfoque de rehidratación parcial tiene sus propios problemas y compromisos. Plantea algunos desafíos interesantes para el almacenamiento en caché, y la navegación del cliente significa que no podemos suponer que el HTML renderizado por el servidor para las partes inertes de la aplicación esté disponible sin una carga de página completa.

Renderización trisomórfica

Si los service workers son una opción para ti, considera la renderización trisomórfica. Esta técnica te permite usar la renderización del servidor de transmisión para las navegaciones iniciales o que no son de JavaScript y, luego, hacer que tu service worker se encargue de la renderización de HTML para las navegaciones después de que se haya instalado. Esto puede mantener actualizados los componentes y las plantillas almacenados en caché, y habilitar navegaciones de estilo SPA para renderizar vistas nuevas en la misma sesión. Este enfoque funciona mejor cuando puedes compartir el mismo código de creación de plantillas y enrutamiento entre el servidor, la página del cliente y el service worker.

Renderización trisomórfica, que muestra un navegador y un service worker que se comunican con el servidor.

Consideraciones de SEO

Cuando eligen una estrategia de renderización web, los equipos suelen considerar el impacto del SEO. La renderización del servidor es una opción popular para ofrecer una experiencia "de aspecto completo" que los rastreadores puedan interpretar. Los rastreadores pueden comprender JavaScript, pero a menudo tienen limitaciones en la forma en que renderizan. La renderización del cliente puede funcionar, pero a menudo necesita pruebas y sobrecarga adicionales. Más recientemente, la renderización dinámica también se convirtió en una opción que vale la pena considerar si tu arquitectura depende en gran medida de JavaScript del cliente.

Conclusión

Cuando decidas un enfoque para la renderización, mide y comprende cuáles son tus cuellos de botella. Considera si la renderización estática o la renderización del servidor pueden llevarte hasta allí. Está bien enviar principalmente HTML con un mínimo de JavaScript para obtener una experiencia interactiva. Esta es una infografía útil que muestra el espectro cliente-servidor:

Opciones de renderización y sus compensaciones.

Créditos

Gracias a todos por sus opiniones e inspiración:

Jeffrey Posnick, Houssein Djirdeh, Shubhie Panicker, Chris Harrelson y Sebastian Markbåge.