Patrones de Renderizado

Image by Susie Lu, take from the Addy Osmani's post

lo que el navegador hace cuando nadie lo mira

Demo

Mientras la industria habla de IA…

…el navegador lleva años evolucionando en silencio:

  • Nuevas propiedades CSS que cambian cómo se calcula el layout.
  • Nuevas APIs que prerenderizan páginas enteras antes del click.
  • Nuevas formas de transicionar entre estados sin una línea de JavaScript de animación.

La diferencia entre una web mediocre y una web instantánea raramente está en el framework. Casi siempre está en entender la plataforma.

El pipeline del navegador

Las únicas métricas que importan

  • LCP ¿Cuándo se ve algo? → ≤ 2.5 s
  • INP ¿Cuándo responde algo? → ≤ 200 ms
  • CLS ¿Se mueve el contenido? → ≤ 0.1
  • TTFB ¿Cuándo responder el servidor? → ≤ 800 ms

Core Web Vitals

El listón real: el sitio aprueba CWV solo si el 75% de las visitas cumple los umbrales en el p75 del CrUX

¿Dónde Renderizar?

¿Dónde Renderizar?

Client-side Rendering

Server-side Rendering

Static Rendering

Incremental Static Generation

Progressive Hydration

Streaming Server-Side Rendering

React Server Components

Selective Hydration

Islands Architecture

Client-Side Rendering (CSR)

Server-Side Rendering (SSR)

Static Rendering

Progressive Hydration

Streaming Server-Side Rendering

Selective Hydration

Los "trucos" del navegador

Lazy Loading: Import on Interaction

No cargues lo que el usuario no va a tocar.

Patrón "Facade": vista estática (imagen + botón fake). Cargar el módulo real al primer click.

// Antes: 580 KB de embed de YouTube en cada artículo
<YouTubeEmbed videoId="abc123" />

// Después: 2 KB de placeholder. 580 KB solo si hay click.
<LiteYouTubeEmbed videoId="abc123" />

Lazy Loading: Import on Visibility

Carga componentes cuando entran o están cerca del viewport

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      import('./HeavyChart.js').then(({ render }) => render(entry.target));
      observer.unobserve(entry.target);
    }
  });
}, { rootMargin: '200px' });

document.querySelectorAll('[data-chart]').forEach(el => observer.observe(el));

El navegador ya hace loading="lazy" para imágenes, videos e iframes.
Esto es lo mismo para tu código.

DOM State Caching: navegación instantánea sin framework

SPAs con pestañas: no destruir el DOM al cambiar de vista

.tab[hidden-view] {
  content-visibility: hidden;
}

El problema: `display: none` reflow al volver.

Destruir = perder estado = reconstruir

El navegador:

  • Detiene layout, estilo y pintura.
  • Conserva en memoria el árbol de renderizado ya compilado.
  • Al reactivar: restauración instantánea, cero CPU.

content-visibility, la propiedad CSS más infravalorada

Dos líneas. Resultados absurdos.

.heavy-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 500px;
}

content-visibility: auto, el navegador omite layout, estilo y pintura de elementos fuera de pantalla.
 

contain-intrinsic-size (OBLIGATORIO): define el tamaño estimado. Sin esto, el elemento colapsa a `0px` afecta a CLS al hacer scroll.

LIVE DEMO

contain: layout, el reflow que no sale del contenedor

100 tarjetas. 5 columnas. Editas una tarjeta.

.kanban-column {
  contain: layout;
}

Sin `contain: layout`: el navegador recalcula las 100 tarjetas, no puede garantizar que el cambio no afecte a las demás columnas.

Con `contain: layout`: recalcula solo las 20 tarjetas de esa columna. La promesa al navegador: nada dentro puede cambiar la geometría de nada fuera.

LIVE DEMO

bfcache, la optimización gratuita que todos rompen

El Back/Forward Cache guarda un snapshot completo en memoria: DOM, heap de JS, conexiones.

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Vienes del bfcache — refresca solo lo que necesites
  }
});

Adelante/atrás = restauración en milisegundos.

No hay TTFB. No hay parse. No hay nada.

LIVE DEMO

¿Por qué la mayoría de SPAs lo rompen sin saberlo?

  • `Cache-Control: no-store` (rompe el bfcache).
  • Event listeners de `unload`.
  • Conexiones WebSocket / IndexedDB sin liberar.

View Transitions API

Transiciones de app nativas en la web.

Sin librerías. Sin GSAP. Sin trucos.

// SPA: cualquier cambio de estado animable
document.startViewTransition(() => updateDOM());

// MPA: transiciones cross-document
@view-transition { navigation: auto; }
```

```css
::view-transition-old(hero) { animation: slide-out 300ms; }
::view-transition-new(hero) { animation: slide-in  300ms; }

.product-image { view-transition-name: hero; }

LIVE DEMO

View Transitions API

Transiciones de app nativas en la web.

Sin librerías. Sin GSAP. Sin trucos.

::view-transition-old(hero) { animation: slide-out 300ms; }
::view-transition-new(hero) { animation: slide-in  300ms; }

.product-image { view-transition-name: hero; }

LIVE DEMO

// SPA: cualquier cambio de estado animable
document.startViewTransition(() => updateDOM());

// MPA: transiciones cross-document
@view-transition { navigation: auto; }

JS

CSS

El navegador toma un screenshot del estado actual, ejecuta el cambio, y anima la transición en una capa del compositor, fuera del hilo principal.

Speculation Rules API

El navegador prerenderiza la página entera antes de que hagas click.

LIVE DEMO

<script type="speculationrules">
{
  "prerender": [{
    "source": "document",
    "where": { "selector_matches": "a.product-card" },
    "eagerness": "moderate"
  }]
}
</script>

Prefetch = descarga el HTML.
Prerender = ejecuta la página entera: parse, CSS, JS, fetch de datos.

HTML-in-Canvas, el DOM + WebGL

La propuesta (Chromium experimental)

<canvas layoutsubtree>
  <article>Texto real, buscable, accesible</article>
</canvas>

El futuro de los editores visuales en la web. Y de los juegos con UI accesible.

HTML

const ctx = canvas.getContext('2d');
ctx.drawElementImage(articleEl, x, y); //dibuja el DOM dentro del canvas
canvas.onpaint = () => redraw();       //redibuja cuando cambia el HTML

JS

Demos

Cómo funciona el navegador

Patrones de Renderizado

By Joan León

Patrones de Renderizado

En esta charla veremos los patrones de renderización que pueden mejorar significativamente la experiencia de la usuaria o usuario en aplicaciones web. Comenzaremos por examinar los desafíos comunes asociados con la renderización y cómo afectan al rendimiento y la interactividad. Luego, presentaremos una serie de patrones y técnicas eficientes para optimizar la renderización, incluyendo la renderización del lado del servidor, el lazy loading y el virtual scrolling. Además, veremos cómo utilizar los patrones de renderización para lograr una carga rápida de la página, una navegación fluida y una experiencia de usuaria/o sin interrupciones. Esta charla proporcionará a los desarrolladores las herramientas y el conocimiento necesarios para crear aplicaciones web altamente eficientes y responsivas, ofreciendo una experiencia excepcional.

  • 35