Core Web Vitals: Guía Técnica Completa para Optimizar tu Sitio Web en 2026

core web vitals

Desde que Google incorporó los Core Web Vitals como factor de posicionamiento en 2021, medir y optimizar la experiencia del usuario se convirtió en una prioridad técnica para cualquier desarrollador o administrador de sitios web. En esta guía técnica vas a entender qué miden estas métricas, cuáles son los umbrales que Google exige, y qué estrategias concretas podés aplicar para superarlos.

¿Qué son los Core Web Vitals?

Los Core Web Vitals son un subconjunto de métricas de Web Vitals definidas por Google para cuantificar la experiencia del usuario en tres dimensiones fundamentales:

  • Velocidad de carga — ¿qué tan rápido carga el contenido principal?
  • Interactividad — ¿qué tan rápido responde la página a las acciones del usuario?
  • Estabilidad visual — ¿el contenido se mueve de forma inesperada mientras carga?

Google utiliza datos reales de usuarios (RUM — Real User Monitoring) recopilados a través del Chrome User Experience Report (CrUX) para evaluar estas métricas. Esto significa que no alcanza con tener buenos resultados en laboratorio: el comportamiento de tus usuarios reales es lo que importa para el ranking.

Importante: Los Core Web Vitals son métricas de campo (field metrics), no de laboratorio. Google evalúa el percentil 75 de todos los usuarios de tu sitio en los últimos 28 días.

Las tres métricas principales

1. LCP — Largest Contentful Paint

El LCP mide el tiempo que tarda en renderizarse el elemento de contenido más grande visible en el viewport inicial. Generalmente este elemento es una imagen hero, un video de portada, o un bloque de texto prominente.

ClasificaciónValor LCP
✅ Bueno≤ 2.5 segundos
⚠️ Necesita mejora2.5s – 4.0 segundos
❌ Deficiente> 4.0 segundos

¿Qué elementos pueden ser el LCP?

  • Elementos <img>
  • Imágenes dentro de <svg>
  • Elementos <video> (thumbnail de portada)
  • Elementos con background-image via CSS
  • Bloques de texto: <p>, <h1> a <h6>

2. INP — Interaction to Next Paint

El INP (Interaction to Next Paint) reemplazó al FID (First Input Delay) en marzo de 2024. Mide la latencia de todas las interacciones discretas del usuario con la página — clics, taps y pulsaciones de teclado — y reporta el valor del percentil 98.

ClasificaciónValor INP
✅ Bueno≤ 200 ms
⚠️ Necesita mejora200ms – 500ms
❌ Deficiente> 500ms

A diferencia del FID, el INP no mide solo la primera interacción sino todas las interacciones a lo largo de la visita. Esto lo hace mucho más representativo del comportamiento real de la página.

3. CLS — Cumulative Layout Shift

El CLS mide la suma de todos los layout shifts inesperados que ocurren durante la vida de la página. Un layout shift sucede cuando un elemento visible cambia su posición de un frame al siguiente sin que haya habido una interacción del usuario.

ClasificaciónValor CLS
✅ Bueno≤ 0.1
⚠️ Necesita mejora0.1 – 0.25
❌ Deficiente> 0.25

El score del CLS se calcula como: impact fraction × distance fraction por cada shift, acumulados en ventanas de tiempo de 5 segundos.

Herramientas para medir los Core Web Vitals

Herramientas de campo (datos reales)

  • Google Search Console — Informe de experiencia de la página, datos de CrUX agrupados por URL
  • Chrome UX Report (CrUX) — API pública con datos históricos a nivel de origen y URL
  • PageSpeed Insights — Combina datos de campo de CrUX con análisis de laboratorio de Lighthouse

Herramientas de laboratorio (simulación)

  • Lighthouse — Integrado en Chrome DevTools (pestaña Performance)
  • WebPageTest — Pruebas desde múltiples ubicaciones y dispositivos reales
  • Chrome DevTools Performance Panel — Grabación detallada del timeline de renderizado

Monitoreo programático

Podés medir los Core Web Vitals directamente en tu código JavaScript usando la librería oficial web-vitals:

import { onLCP, onINP, onCLS } from 'web-vitals';

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    delta: metric.delta,
    id: metric.id,
  });

  navigator.sendBeacon('/analytics', body);
}

onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);

Cómo optimizar el LCP

El LCP es frecuentemente el métrico más difícil de mejorar porque depende de múltiples capas de la stack: servidor, red, y renderizado del navegador.

1. Identificar el elemento LCP

Antes de optimizar, confirmá qué elemento está siendo identificado como LCP usando Chrome DevTools:

// En la consola del navegador
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('LCP element:', entry.element);
    console.log('LCP time:', entry.startTime);
  }
}).observe({ type: 'largest-contentful-paint', buffered: true });

2. Priorizar la carga de la imagen LCP

Si el LCP es una imagen, usá el atributo fetchpriority="high" para indicarle al navegador que la descargue con máxima prioridad:

<img 
  src="hero-image.webp" 
  alt="Descripción de la imagen principal"
  width="1200" 
  height="600"
  fetchpriority="high"
  loading="eager"
>

Nunca uses loading="lazy" en la imagen LCP. El lazy loading retrasa la descarga hasta que el elemento está cerca del viewport, lo cual perjudica directamente el LCP.

3. Precargar recursos críticos

<!-- Precargar la imagen LCP -->
<link rel="preload" as="image" href="hero-image.webp" fetchpriority="high">

<!-- Precargar la fuente principal -->
<link rel="preload" as="font" href="fuente-principal.woff2" crossorigin>

<!-- Preconectar a dominios de terceros críticos -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

4. Optimizar imágenes

  • Convertí imágenes a WebP o AVIF — hasta 50% menos peso que JPEG/PNG
  • Usá imágenes responsive con srcset y sizes
  • Implementá CDN con edge caching para reducir la latencia
  • Configurá compresión en el servidor (Brotli preferentemente sobre gzip)
<img
  src="hero-800.webp"
  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
  sizes="(max-width: 600px) 400px, (max-width: 900px) 800px, 1200px"
  alt="Imagen hero del sitio"
  width="1200"
  height="600"
  fetchpriority="high"
>

5. Reducir el Time to First Byte (TTFB)

El TTFB es el tiempo hasta que el navegador recibe el primer byte del servidor. Un TTFB alto arrastra directamente el LCP. Para reducirlo:

  • Implementá caché de página completa (full-page cache) en WordPress o tu CMS
  • Usá un CDN con edge servers próximos a tus usuarios
  • Optimizá queries de base de datos lentas
  • Considerá arquitecturas con SSG (Static Site Generation) o ISR

Cómo optimizar el INP

El INP es un problema de JavaScript. Las causas más frecuentes son: long tasks que bloquean el hilo principal, event handlers ineficientes, y exceso de trabajo en la fase de renderizado.

1. Diagnosticar long tasks

Usá el Performance Panel de Chrome DevTools y buscá barras rojas sobre el Main Thread. Cualquier tarea que supere los 50ms es una long task y contribuye al INP.

// Detectar long tasks programáticamente
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.duration > 50) {
      console.warn('Long task detectada:', {
        duration: entry.duration,
        startTime: entry.startTime
      });
    }
  }
}).observe({ type: 'longtask', buffered: true });

2. Dividir el trabajo pesado (code splitting)

En lugar de ejecutar grandes bloques de código sincrónicamente, dividílos usando setTimeout o el scheduler API:

// Ceder el control al hilo principal entre tareas pesadas
function procesarDatosEnPartes(items) {
  let indice = 0;

  function procesarLote() {
    const limite = Math.min(indice + 100, items.length);
    
    while (indice < limite) {
      procesarItem(items[indice]);
      indice++;
    }

    if (indice < items.length) {
      // Ceder el control antes del siguiente lote
      setTimeout(procesarLote, 0);
    }
  }

  procesarLote();
}

3. Diferir JavaScript no crítico

<!-- Scripts no críticos con defer -->
<script src="analytics.js" defer></script>

<!-- Scripts de terceros con lazy loading -->
<script>
  // Cargar chat widget solo cuando el usuario interactúa
  document.addEventListener('scroll', () => {
    import('./chat-widget.js').then(module => module.init());
  }, { once: true });
</script>

4. Evitar layouts innecesarios durante interacciones

Leer propiedades de layout (como offsetHeight, getBoundingClientRect()) y luego escribir en el DOM en el mismo ciclo causa layout thrashing. Separá las lecturas de las escrituras:

// ❌ MAL: layout thrashing
elements.forEach(el => {
  const height = el.offsetHeight; // fuerza layout
  el.style.height = (height * 2) + 'px'; // invalida layout
});

// ✅ BIEN: batch reads y writes
const heights = elements.map(el => el.offsetHeight); // todas las lecturas juntas
elements.forEach((el, i) => {
  el.style.height = (heights[i] * 2) + 'px'; // todas las escrituras juntas
});

Cómo optimizar el CLS

El CLS es el métrico más «controlable» de los tres. La mayoría de los layout shifts tienen causas bien conocidas y soluciones directas.

1. Siempre especificá dimensiones en imágenes y videos

<!-- ❌ Sin dimensiones: causa layout shift cuando carga -->
<img src="foto.jpg" alt="Foto del producto">

<!-- ✅ Con dimensiones: el navegador reserva el espacio -->
<img src="foto.jpg" alt="Foto del producto" width="800" height="600">

El navegador calcula el aspect ratio automáticamente con width y height, por lo que la imagen puede seguir siendo responsive con CSS sin causar shifts.

2. Reservar espacio para contenido dinámico

/* Reservar espacio para un banner que carga asincrónicamente */
.banner-container {
  min-height: 90px; /* altura esperada del banner */
}

/* Reservar espacio para skeleton screens */
.card-skeleton {
  height: 200px;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
}

3. Cuidado con las fuentes web (FOUT / FOIT)

El cambio de fuente al cargar (Flash of Unstyled Text) puede causar layout shifts si las métricas de la fuente son muy distintas a la fallback.

/* Usar font-display: optional para eliminar shifts */
@font-face{ 
  font-family: 'MiFuente';
  src: url('fuente.woff2') format('woff2');
  font-display:swap; /* no muestra la fuente custom si tarda en cargar */
 }

/* Alternativa: ajustar métricas de la fuente fallback */
@font-face{ font-display:swap;
  font-family: 'MiFuente-Fallback';
  src: local('Arial');
  ascent-override: 90%;
  descent-override: 20%;
  line-gap-override: 0%;
 }

4. Animaciones que no causan layout

Limitá las animaciones a propiedades que el navegador puede animar en el compositor, sin pasar por el layout:

  • Seguras (compositor): transform, opacity, filter, will-change
  • Inseguras (causan layout): top, left, width, height, margin, padding
/* ❌ MAL: animar left causa layout y puede generar CLS */
.elemento {
  transition: left 0.3s ease;
}

/* ✅ BIEN: animar transform corre en el compositor */
.elemento {
  transition: transform 0.3s ease;
}
.elemento.activo {
  transform: translateX(100px);
}

Monitoreo continuo de Core Web Vitals

La optimización de Core Web Vitals no es un trabajo puntual sino un proceso continuo. Cada deploy, nuevo plugin, o script de terceros puede degradar las métricas.

Estrategia de monitoreo recomendada

  1. Google Search Console — Revisión semanal del informe «Experiencia de página». Configurá alertas de email para degradaciones.
  2. CrUX API + BigQuery — Para sitios con alto tráfico, consultá los datos históricos de CrUX directamente para análisis granular por URL.
  3. RUM en producción — Implementá la librería web-vitals y enviá los datos a tu plataforma de analytics. Google Analytics 4 incluye métricas de Web Vitals de forma nativa.
  4. Synthetic monitoring — Configurá pruebas automatizadas con Lighthouse CI en tu pipeline de CI/CD para detectar regresiones antes de que lleguen a producción.

Lighthouse CI en GitHub Actions

# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Lighthouse CI
        uses: treosh/lighthouse-ci-action@v11
        with:
          urls: |
            https://tu-sitio.com/
            https://tu-sitio.com/blog/
          budgetPath: ./budget.json
          uploadArtifacts: true
// budget.json — umbrales mínimos aceptables
[
  {
    "path": "/*",
    "timings": [
      { "metric": "largest-contentful-paint", "budget": 2500 },
      { "metric": "cumulative-layout-shift", "budget": 0.1 },
      { "metric": "total-blocking-time", "budget": 300 }
    ]
  }
]

Los Core Web Vitals son hoy una parte indisociable del SEO técnico. Google los usa explícitamente como señal de ranking y, más allá del posicionamiento, impactan directamente en la conversión y la retención de usuarios.

Para resumir las prioridades de acción:

  1. LCP > 2.5s → Optimizá imágenes, usá fetchpriority="high", reducí el TTFB con caché y CDN
  2. INP > 200ms → Investigá long tasks en el Performance Panel, dividí trabajo pesado, diferí scripts no críticos
  3. CLS > 0.1 → Dimensioná imágenes, reservá espacio para contenido dinámico, usá font-display: optional

La clave del éxito a largo plazo es combinar optimizaciones puntuales con un sistema de monitoreo continuo que detecte regresiones antes de que afecten a tus usuarios reales y tu ranking en Google.

¿Tenés preguntas sobre cómo implementar alguna de estas optimizaciones en tu stack particular? Dejá tu consulta en los comentarios.

Comentarios
advertise width me