Métricas de rendimiento Core Web Vitals — LCP, INP, CLS
15 min read

Construyendo para el Rendimiento: La Guía Completa de Core Web Vitals

#Performance #Core Web Vitals #Astro

El rendimiento es una característica central, no una ocurrencia tardía. Un retraso de 100ms en el tiempo de carga puede reducir las conversiones un 7%. Una página que carga en 1 segundo tiene una tasa de conversión 3 veces mayor que una que carga en 5 segundos. Y desde 2021, Google usa Core Web Vitals como señal directa de ranking — los sitios lentos literalmente ranquean más bajo.

Esta guía cubre todo lo necesario para lograr puntuaciones excelentes: las tres métricas que Google mide, las decisiones arquitectónicas que hacen a Astro únicamente rápido, y las técnicas específicas de optimización para imágenes, CSS, JavaScript, fuentes y scripts de terceros.

El rendimiento no es un problema técnico. Es una restricción de diseño que afecta cada decisión que tomas.

Alex Russell Ingeniero de Rendimiento, Google Chrome

Core Web Vitals: Las tres métricas

Google mide tres métricas específicas que representan la experiencia del usuario en carga, interactividad y estabilidad visual.

2,5s
Objetivo LCP
Umbral bueno
200ms
Objetivo INP
Umbral bueno
0,1
Objetivo CLS
Umbral bueno
Visualización de la métrica LCP
LCP — Largest Contentful Paint

Mide qué tan rápido aparece el elemento visible más grande (imagen hero, titular, miniatura de video) en pantalla. Objetivo: menos de 2,5 segundos. Esta es la métrica que los usuarios más sienten — determina qué tan rápido la página "se ve cargada". El elemento LCP es típicamente una imagen hero, un encabezado grande o un póster de video.

Visualización de retraso de interacción INP
INP — Interaction to Next Paint

Mide la capacidad de respuesta — qué tan rápido reacciona la página a la entrada del usuario (clics, toques, pulsaciones de teclas). Objetivo: menos de 200 milisegundos. INP reemplazó a FID en marzo de 2024 porque mide TODAS las interacciones durante el ciclo de vida de la página, no solo la primera. JavaScript pesado es el principal enemigo de buenas puntuaciones INP.

Visualización de cambio de layout CLS
CLS — Cumulative Layout Shift

Mide la estabilidad visual — cuánto se desplaza inesperadamente el contenido de la página durante la carga. Objetivo: menos de 0,1. Nada frustra más a los usuarios que hacer clic en un botón y que la página salte, haciendo que cliqueen otra cosa. Imágenes sin dimensiones, fuentes que cargan tarde y anuncios inyectados son los principales culpables.

Por qué Astro está construido para el rendimiento

La arquitectura de Astro es fundamentalmente diferente de React, Next.js o Nuxt. Envía cero JavaScript por defecto. Cada página se renderiza como HTML estático en tiempo de build, sin sobrecarga de hidratación.

Framework JS por defecto Recommended Hidratación Impacto LCP
Astro (estático) 0 KB Ninguna (HTML estático) Excelente
Astro (islands) Solo por island Selectiva Excelente
Next.js (App Router) 85–120 KB Página completa Bueno
Nuxt 3 70–100 KB Página completa Bueno
Create React App 150–250 KB Página completa Deficiente
WordPress (promedio) 200–400 KB Dependiente de plugins Deficiente

La arquitectura de islands de Astro significa que los componentes interactivos (un formulario, un carrusel, una búsqueda) se hidratan individualmente. El resto de la página — header, footer, contenido, navegación — permanece como HTML puro. Por eso los sitios Astro consistentemente puntúan 95–100 en Lighthouse sin ningún esfuerzo de optimización.

Optimización LCP

El Largest Contentful Paint es usualmente el mayor desafío. Aquí las estrategias ordenadas por impacto:

Optimizar el elemento LCP

Identifica tu elemento LCP (generalmente imagen hero). Precárgalo con <link rel="preload">. Sírvelo en WebP/AVIF. Establece width/height explícitos. Nunca uses lazy-load en la imagen LCP.

Eliminar bloqueo de render

CSS crítico inline. Diferir hojas de estilo no críticas. Eliminar CSS no usado. Usar <link rel="preconnect"> para orígenes externos.

Reducir respuesta del servidor

Usar CDN (Netlify, Vercel, Cloudflare). Habilitar compresión (Brotli > gzip). Establecer cache headers de largo plazo para assets estáticos.

Optimizar carga de fuentes

Usar font-display: swap. Precargar archivos de fuentes críticas. Hacer subset de fuentes a caracteres necesarios. Self-host en lugar de Google Fonts para menos conexiones.

Optimización de imágenes en detalle

Las imágenes son el elemento LCP en el 70% de las páginas web. Optimizarlas correctamente tiene el mayor impacto individual en el rendimiento percibido.

---
/* El componente Image integrado de Astro maneja la optimización */
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---
<!-- WebP/AVIF automático, srcset responsivo, width/height -->
<Image
  src={heroImage}
  alt="Descripción de imagen hero"
  widths={[400, 800, 1200]}
  sizes="(max-width: 768px) 100vw, 1200px"
  loading="eager"
  fetchpriority="high"
/>
<!-- Imagen responsiva manual con art direction -->
<picture>
  <source
    type="image/avif"
    srcset="/images/hero-400.avif 400w,
           /images/hero-800.avif 800w,
           /images/hero-1200.avif 1200w"
    sizes="(max-width: 768px) 100vw, 1200px"
  />
  <source
    type="image/webp"
    srcset="/images/hero-400.webp 400w,
           /images/hero-800.webp 800w,
           /images/hero-1200.webp 1200w"
    sizes="(max-width: 768px) 100vw, 1200px"
  />
  <img src="/images/hero-800.jpg" alt="Hero" width="1200" height="630"
       loading="eager" fetchpriority="high" decoding="async" />
</picture>
<!-- En <head>: precargar imagen LCP para entrega más rápida -->
<link
  rel="preload"
  as="image"
  type="image/webp"
  href="/images/hero-1200.webp"
  imagesrcset="/images/hero-400.webp 400w,
               /images/hero-800.webp 800w,
               /images/hero-1200.webp 1200w"
  imagesizes="(max-width: 768px) 100vw, 1200px"
  fetchpriority="high"
/>

Optimización INP

Interaction to Next Paint mide qué tan responsiva se siente tu página. Cada clic, toque y pulsación de tecla es evaluado.

La causa principal de mal INP es demasiado JavaScript en el main thread. Cada KB de JS debe ser parseado, compilado y ejecutado antes de que el navegador pueda responder a la entrada del usuario. El zero-JS por defecto de Astro resuelve esto para la mayoría de páginas. Para islands interactivas, mantén cada island bajo 50 KB. Usa imports dinámicos: client:visible hidrata solo cuando el componente entra en el viewport.

JavaScript de larga ejecución bloquea el main thread. Si una función toma 300ms, el navegador no puede responder a clics durante ese tiempo. Divide tareas largas en chunks más pequeños usando requestAnimationFrame() o setTimeout(0). Esto devuelve el control al navegador entre chunks, manteniendo las interacciones ágiles. Objetivo: ninguna tarea individual debe exceder 50ms.

Los campos de búsqueda, controles de filtro y scroll handlers disparan eventos rápidamente. Sin debouncing, cada pulsación de tecla dispara operaciones costosas (consultas DOM, re-renders, llamadas API). Aplica debounce a los handlers de input con 150–300ms de retraso. Usa requestAnimationFrame() para scroll handlers. Esto reduce drásticamente el trabajo por interacción.

Presupuesto de JavaScript

Recurso Presupuesto Impacto Recommended
JS total por ruta < 170 KB gzipped INP + LCP
Island individual < 50 KB gzipped INP
Scripts de terceros < 50 KB total INP + LCP
Trabajo del main thread < 50ms por tarea INP
Total Blocking Time < 200ms INP (proxy lab)

Optimización CLS

Los cambios de layout ocurren cuando elementos visibles cambian de posición después del render inicial. Son el problema de rendimiento más frustrante desde la perspectiva del usuario. Un ejemplo de animaciones que evitan estos problemas son las animaciones SVG hero con CSS puro, que usan exclusivamente transform y opacity para lograr 60fps sin impactar el layout.

Eliminando cambios de layout

  1. Siempre establecer dimensiones de imagen

    Cada <img> y <video> debe tener atributos width y height explícitos (o CSS aspect-ratio). El navegador reserva el espacio correcto antes de que cargue el asset, previniendo cambios.

  2. Usar font-display: swap con cuidado

    Aunque swap evita texto invisible, el cambio de fuente mismo puede causar un shift si la fuente fallback y la web font tienen métricas diferentes. Usa la propiedad CSS size-adjust en @font-face para igualar las métricas de la fuente fallback.

  3. Reservar espacio para contenido dinámico

    Anuncios, embeds, banners de cookies y componentes lazy-loaded deben tener min-height o aspect-ratio definidos antes de cargar. Nunca inyectes contenido encima del contenido existente sin interacción del usuario.

  4. Evitar banners inyectados arriba

    Un banner de consentimiento de cookies que empuja la página hacia abajo causa un pico masivo de CLS. Usa posicionamiento fixed/sticky o patrones de overlay que no desplacen el contenido existente.

  5. Animar solo con transform

    Las propiedades CSS como top, left, width y height disparan recálculos de layout. Usa transform: translateX/Y y opacity para animaciones. Estas se componen en la GPU y nunca causan cambios de layout.

Estrategia de rendimiento CSS

<!-- CSS crítico inline: estilos para contenido above-the-fold -->
<style>
  /* Reset base, layout, header, hero, tipografía */
  /* Mantener bajo 14 KB (un TCP round-trip) */
  :root { --color-bg: #f9fafb; --color-text: #111827; }
  body { font-family: system-ui; color: var(--color-text); }
  .header { display: flex; align-items: center; }
  .hero { min-height: 60vh; }
</style>
<!-- Diferir CSS no crítico con truco de media print -->
<link
  rel="stylesheet"
  href="/styles/below-fold.css"
  media="print"
  onload="this.media='all'"
/>
<noscript>
  <link rel="stylesheet" href="/styles/below-fold.css" />
</noscript>
/* Carga de fuentes optimizada con overrides de métricas */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-var.woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
  size-adjust: 107%;       /* igualar métricas del fallback */
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

Flujo de trabajo de medición

Saber qué medir y cómo medirlo es la mitad de la batalla. Usa herramientas de laboratorio para debugging y datos de campo para la verdad.

Lighthouse (Chrome DevTools, CI): tests sintéticos en condiciones controladas. Ideal para identificar problemas específicos. Ejecutar en modo incógnito, sin extensiones, con conexión throttled (Slow 4G). Enfocarse en el desglose del score de rendimiento, no solo el número general. WebPageTest ofrece comparaciones de filmstrip, diagramas de waterfall y tests multi-paso.

Chrome UX Report (CrUX): datos reales de usuarios recopilados de usuarios de Chrome que optaron por participar. Esto es lo que Google realmente usa para el ranking. Accede vía PageSpeed Insights, Search Console o la API de CrUX. Los datos de campo reflejan tus usuarios reales en sus dispositivos y conexiones reales — es la verdad fundamental.

Configura Lighthouse CI automatizado en tu pipeline de deployment. Cada PR ejecuta una auditoría de rendimiento. Establece presupuestos: LCP bajo 2,5s, INP bajo 200ms, CLS bajo 0,1, JS total bajo 170 KB. Falla el build si se exceden los presupuestos. Monitorea datos CrUX mensualmente vía Search Console para detectar regresiones en el rendimiento real.

La checklist de rendimiento

Imágenes

Formatos WebP/AVIF. Dimensiones explícitas. srcset responsivo. LCP con eager. Below-fold con lazy.

Fuentes

WOFF2 self-hosted. font-display: swap. Precargar fuentes críticas. size-adjust para prevención de CLS.

CSS

CSS crítico inline. No crítico diferido. Ruta crítica bajo 50 KB. Sin estilos no usados.

JavaScript

Bajo 170 KB por ruta. Islands para interactividad. Imports dinámicos. Sin bloqueo del main thread.

Red

Entrega CDN. Compresión Brotli. Cache headers. Preconnect a orígenes externos.

Monitoreo

Lighthouse CI en pipeline. Datos CrUX mensuales. Presupuestos de rendimiento aplicados.

Errores comunes

Frequently Asked Questions

Cronograma de rendimiento: Orden de optimización

Victorias rápidas

Activar CDN + compresión. Añadir dimensiones explícitas a imágenes. Self-host fuentes con font-display: swap. Eliminar CSS/JS no usado. Solo esto lleva a la mayoría de sitios a la banda "Bueno".

Pipeline de imágenes

Convertir imágenes a WebP/AVIF. Generar srcset responsivo. Precargar imagen LCP. Lazy load imágenes below-fold. Esta fase típicamente ahorra 1–3 segundos en LCP.

Dieta de JavaScript

Auditar scripts de terceros. Reemplazar librerías pesadas con alternativas más ligeras. Usar Astro islands con client:visible para hidratación diferida. Establecer y aplicar presupuestos JS.

Monitoreo continuo

Configurar Lighthouse CI en pipeline de deployment. Monitorear datos CrUX mensualmente. Crear presupuestos de rendimiento. Detectar regresiones antes de que lleguen a los usuarios.