¡Estamos construyendo algo increíble! Nuestro sitio está en desarrollo. ¡Vuelve pronto!
development

Core Web Vitals 2025: La Guía Definitiva para Aprobar con 100/100

Guía completa de Core Web Vitals: LCP, FID, CLS. Aprende a optimizar tu sitio web para Google, con ejemplos reales en Next.js y métricas medibles.

Nandark Team
10 min de lectura
#LCP optimizacion#FID mejora#CLS fix#performance next js#lighthouse 100

Por Qué Tu Sitio Lento Te Está Costando Dinero (Y Cómo Arreglarlo)

En 2025, Google no solo mira el contenido de tu sitio. Mira cómo se siente usarlo.

Un sitio que carga en 5 segundos tiene 90% más probabilidad de que el usuario se vaya sin comprar. Cada segundo de delay = 7% menos conversiones (datos de Amazon).

Pero no necesitas adivinar si tu sitio es "rápido". Google definió 3 métricas exactas que determinan si pasas o repruebas: los Core Web Vitals.

Esta guía te enseña exactamente cómo optimizar cada una hasta lograr 100/100 en Lighthouse. Con ejemplos reales de código Next.js.

Los 3 Core Web Vitals Explicados (Sin Jerga Técnica)

1. LCP (Largest Contentful Paint) - "¿Cuánto tarda en verse algo?"

Qué mide: Tiempo hasta que el contenido principal se vuelve visible.

Objetivo: < 2.5 segundos

En español: Si tu hero image tarda 5 segundos en aparecer, tu LCP es 5 segundos. Google dice que eso es inaceptable.

Ejemplo práctico:

// ❌ MAL: Imagen sin optimizar (LCP 4.5s)
<img src="/hero.jpg" alt="Hero" />

// ✅ BIEN: Next.js Image con priority (LCP 1.2s)
import Image from 'next/image';

<Image
  src="/hero.jpg"
  alt="Hero"
  width={1200}
  height={600}
  priority // ⭐ Esto es clave para LCP
  placeholder="blur"
  blurDataURL="data:image/..." // Placeholder mientras carga
/>

2. FID (First Input Delay) → INP en 2024+

Qué mide: Tiempo que tarda el sitio en responder a tu primer click/tap.

Objetivo: < 100ms (ahora reemplazado por INP < 200ms)

En español: Haces click en un botón. ¿Cuánto tarda en pasar algo? Si tarda más de 100ms, se siente "pegajoso".

Causas comunes:

  • JavaScript bloqueando el thread principal
  • Renders pesados en React
  • Hydration lenta

Solución:

// ❌ MAL: Bundle gigante cargado de una
import Chart from 'chart.js'; // 180kb
import Lodash from 'lodash'; // 70kb
import Moment from 'moment'; // 67kb

// ✅ BIEN: Dynamic imports + code splitting
const Chart = dynamic(() => import('chart.js'), {
  loading: () => <Skeleton />,
  ssr: false // No lo necesitas en server
});

// Usa date-fns en lugar de moment (2kb vs 67kb)
import { format } from 'date-fns';

3. CLS (Cumulative Layout Shift) - "¿Por qué se mueve todo?"

Qué mide: Cuánto "salta" el contenido mientras carga.

Objetivo: < 0.1

En español: Estás leyendo. De repente una imagen carga y el texto salta 200px abajo. Acabas clickeando un ad por accidente. Eso es CLS.

Ejemplo del problema:

// ❌ MAL: Sin dimensiones definidas
<img src="/product.jpg" alt="Product" />
// El espacio se reserva solo cuando la imagen carga = SHIFT

// ✅ BIEN: Con aspect ratio reservado
<Image
  src="/product.jpg"
  width={800}
  height={600}
  alt="Product"
  // Next.js reserva el espacio antes de cargar
/>

Cómo Medir tus Core Web Vitals (3 Herramientas)

1. PageSpeed Insights (Datos Reales de Usuarios)

URL: https://pagespeed.web.dev/

Pega tu URL. Google te muestra:

  • Field Data: Métricas reales de usuarios en los últimos 28 días
  • Lab Data: Test sintético en ese momento

Lo que importa: Field Data. Es lo que Google usa para rankear.

2. Lighthouse (Local)

En Chrome DevTools:

  1. Abre DevTools (F12)
  2. Tab "Lighthouse"
  3. Generate Report

Ventaja: Puedes testearlo antes de hacer deploy.

3. Web Vitals Chrome Extension

Descarga: https://chrome.google.com/webstore (busca "Web Vitals")

Muestra LCP, FID, CLS en tiempo real mientras navegas tu sitio.

Optimizando LCP: De 4.5s a 1.2s (Caso Real)

Problema Común: Hero Image Lenta

Antes:

  • Hero image: 2.4MB PNG
  • Sin lazy loading
  • Sin prioridad
  • LCP: 4.5 segundos ❌

Solución paso a paso:

Paso 1: Comprimir la imagen

# Usa squoosh.app o sharp
npm install sharp

node -e "
const sharp = require('sharp');
sharp('hero.png')
  .resize(1920, 1080, { fit: 'cover' })
  .webp({ quality: 80 })
  .toFile('hero.webp');
"

Resultado: 2.4MB → 180KB

Paso 2: Usar Next.js Image con priority

<Image
  src="/hero.webp"
  width={1920}
  height={1080}
  priority // Dile a Next.js que esto es lo más importante
  alt="Hero banner"
/>

Paso 3: Preload fonts (si usas custom fonts)

// app/layout.tsx
export const metadata = {
  // ...
  icons: {
    // Preload critical font
    preload: [
      {
        href: '/fonts/inter-var.woff2',
        as: 'font',
        type: 'font/woff2',
        crossOrigin: 'anonymous',
      },
    ],
  },
};

Después:

  • Hero: 180KB WebP
  • Prioridad máxima
  • Font preloaded
  • LCP: 1.2 segundos

Optimizando FID/INP: JavaScript que No Bloquea

Problema: Dashboard Lento al Interactuar

Síntomas:

  • Click en botón tarda 300ms en responder
  • Typing en input se siente laggy
  • INP: 450ms ❌

Causa: Todo el JavaScript carga de una en el bundle principal.

Solución: Code Splitting Estratégico

// ❌ ANTES: Todo en el bundle principal
import DataTable from '@/components/DataTable'; // 45kb
import ChartComponent from '@/components/Chart'; // 180kb
import PDFViewer from '@/components/PDFViewer'; // 210kb

export default function Dashboard() {
  return (
    <div>
      <DataTable />
      <ChartComponent />
      <PDFViewer />
    </div>
  );
}
// ✅ DESPUÉS: Solo lo crítico en main bundle
import dynamic from 'next/dynamic';

// Lazy load componentes pesados
const DataTable = dynamic(() => import('@/components/DataTable'));
const ChartComponent = dynamic(() => import('@/components/Chart'), {
  loading: () => <Skeleton height={400} />,
  ssr: false, // No lo necesitas en server
});
const PDFViewer = dynamic(() => import('@/components/PDFViewer'), {
  ssr: false,
});

export default function Dashboard() {
  return (
    <div>
      <DataTable />
      <ChartComponent />
      <PDFViewer />
    </div>
  );
}

Resultado:

  • Bundle principal: 340KB → 95KB
  • INP: 450ms → 120ms ✅

Bonus: Usar React.memo para evitar re-renders

// ❌ ANTES: Se re-renderiza en cada keystroke
function UserCard({ user }) {
  return <div>{user.name}</div>;
}

// ✅ DESPUÉS: Solo re-renderiza si user cambió
const UserCard = React.memo(function UserCard({ user }) {
  return <div>{user.name}</div>;
}, (prevProps, nextProps) => {
  // Custom comparator
  return prevProps.user.id === nextProps.user.id;
});

Optimizando CLS: Reserva el Espacio

Problema 1: Imágenes sin dimensiones

// ❌ CLS Score: 0.25
<img src="/product.jpg" alt="Product" />

// ✅ CLS Score: 0.01
<Image
  src="/product.jpg"
  width={600}
  height={400}
  alt="Product"
/>

Problema 2: Ads o embeds que pushean contenido

// ❌ El ad carga y pushea todo 300px abajo
<div>
  <h1>Artículo</h1>
  <Ad /> {/* Sin height definido */}
  <p>Contenido...</p>
</div>

// ✅ Reserva el espacio
<div>
  <h1>Artículo</h1>
  <div style={{ minHeight: '250px' }}>
    <Ad />
  </div>
  <p>Contenido...</p>
</div>

Problema 3: Fonts causando FOIT/FOUT

FOIT (Flash of Invisible Text): El texto es invisible hasta que la font carga. FOUT (Flash of Unstyled Text): El texto aparece con font default, luego cambia.

Ambos causan CLS.

Solución:

/* globals.css */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-var.woff2') format('woff2');
  font-display: swap; /* ⭐ Muestra fallback inmediatamente */
  font-weight: 100 900;
}

Checklist: Cómo Llegar a 100/100 en Lighthouse

Optimización de Imágenes

  • Todas las imágenes en WebP o AVIF
  • Imágenes above-the-fold con priority
  • Lazy loading en imágenes below-the-fold
  • Width y height definidos en TODAS las imágenes
  • Compresión (usa squoosh.app o sharp)

JavaScript & Rendering

  • Code splitting (dynamic imports)
  • Bundle < 200KB (main chunk)
  • React.memo en componentes que re-renderizan mucho
  • Evita useEffect innecesarios
  • No bloquees el main thread (usa Web Workers para cómputo pesado)

Fonts

  • Preload fonts críticas
  • font-display: swap en todas las fonts
  • Subset de fonts (solo los caracteres que usas)
  • Máximo 2 font families

Server & Delivery

  • CDN (Vercel, Cloudflare)
  • Compresión gzip/brotli
  • HTTP/2 o HTTP/3
  • Cache headers correctos

Next.js Específico

  • Usar <Image> en lugar de <img>
  • Server Components cuando sea posible
  • generateStaticParams para páginas dinámicas
  • Edge Runtime para APIs rápidas

Errores Comunes que Destruyen tus Métricas

Error 1: "Voy a usar una librería para TODO"

// ❌ 500KB de librerías para features simples
import _ from 'lodash';
import moment from 'moment';
import axios from 'axios';

// ✅ Usa lo mínimo necesario
import { debounce } from 'lodash-es/debounce'; // Solo lo que necesitas
import { format } from 'date-fns'; // 2KB vs 67KB de moment
// fetch es nativo, no necesitas axios

Error 2: "Todas las imágenes son JPG"

JPG es 2010. WebP es 30% más pequeño. AVIF es 50% más pequeño.

# Convierte todo tu directorio
for img in *.jpg; do
  cwebp -q 80 "$img" -o "${img%.jpg}.webp"
done

Error 3: "No sé qué está haciendo mi sitio lento"

Usa React DevTools Profiler:

  1. Install React DevTools extension
  2. Tab "Profiler"
  3. Record mientras interactúas
  4. Ve qué componentes tardan más

Usa Chrome DevTools Performance:

  1. Tab "Performance"
  2. Record
  3. Reload tu página
  4. Ve el flame chart - identifica bottlenecks

Caso de Éxito: E-commerce 3.2s → 0.9s LCP

Cliente: Tienda online de ropa (Colombia)

Problema inicial:

  • LCP: 3.2s (homepage con 8 hero images)
  • FID: 280ms (mucho JavaScript de analytics)
  • CLS: 0.18 (productos sin dimensiones)
  • Conversión: 1.8%

Optimizaciones aplicadas:

  1. Imágenes:

    • JPG → WebP (-65% tamaño)
    • Hero image con priority
    • Productos con lazy loading
    • Dimensiones definidas en todos
  2. JavaScript:

    • Analytics movido a Google Tag Manager (async)
    • Product slider: dynamic import
    • Checkout form: code split
  3. Fonts:

    • Poppins subset (solo Latin)
    • Preload + font-display: swap

Resultados:

  • LCP: 3.2s → 0.9s ✅
  • FID: 280ms → 45ms ✅
  • CLS: 0.18 → 0.02 ✅
  • Conversión: 1.8% → 2.7% (+50%!)
  • Revenue adicional: ~$18k/mes

Conclusión: Performance es UX, UX es Negocio

En resumen, los Core Web Vitals no son solo una métrica más. Son la representación cuantificable de la experiencia de tus usuarios. Y en un mundo donde cada milisegundo cuenta, optimizarlos es optimizar tu negocio.

Para una configuración SEO completa que incluya Core Web Vitals, consulta nuestra guía de SEO para lanzamiento web.

Si tu sitio actual tiene un LCP > 3s o Lighthouse score < 80, estás dejando dinero en la mesa.


¿Tu Sitio Necesita Optimización?

En Nandark optimizamos Core Web Vitals con resultados medibles. Nuestros clientes típicamente ven:

  • +30-50 puntos en Lighthouse score
  • 2x-3x mejora en velocidad de carga
  • Mejor posicionamiento en Google (Core Web Vitals son factor de ranking)

Servicios relacionados

Solicita una auditoría gratis: Te mostramos exactamente dónde están los cuellos de botella.


Recursos útiles:

Más contenido: Ver todos los artículos

Compartir este artículo

¿Te gustó este artículo? Compártelo con tu red y ayúdanos a llegar a más personas.

Mantente actualizado

Recibe nuestros mejores artículos sobre desarrollo de software, automatización y estrategia digital directamente en tu inbox.

Nandark Team

Escrito por Nandark Team

Equipo de desarrollo en Nandark. Expertos en Next.js, React y automatización empresarial.

¿Listo para transformar tu negocio?

Hablemos sobre cómo Nandark puede ayudarte a desarrollar soluciones de software personalizadas, automatización inteligente y estrategias digitales que impulsen tu crecimiento.