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.
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.
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.
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.
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:
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.
CSS crítico inline. Diferir hojas de estilo no críticas. Eliminar CSS no usado. Usar <link rel="preconnect"> para orígenes externos.
Usar CDN (Netlify, Vercel, Cloudflare). Habilitar compresión (Brotli > gzip). Establecer cache headers de largo plazo para assets estáticos.
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
- 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.
- 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.
- 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.
- 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.
- 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
Formatos WebP/AVIF. Dimensiones explícitas. srcset responsivo. LCP con eager. Below-fold con lazy.
WOFF2 self-hosted. font-display: swap. Precargar fuentes críticas. size-adjust para prevención de CLS.
CSS crítico inline. No crítico diferido. Ruta crítica bajo 50 KB. Sin estilos no usados.
Bajo 170 KB por ruta. Islands para interactividad. Imports dinámicos. Sin bloqueo del main thread.
Entrega CDN. Compresión Brotli. Cache headers. Preconnect a orígenes externos.
Lighthouse CI en pipeline. Datos CrUX mensuales. Presupuestos de rendimiento aplicados.
Errores comunes
Frequently Asked Questions
Lighthouse ejecuta en tu máquina rápida de desarrollo con throttling simulado. Los usuarios reales están en teléfonos Android de 3 años con conexiones 3G. Siempre verifica los datos de campo CrUX (vía PageSpeed Insights o Search Console). El percentil 75 de usuarios reales es lo que Google usa para ranking — no tu score local de Lighthouse.
No. La imagen LCP (generalmente el hero) debe cargarse con eager y fetchpriority="high". Solo aplica lazy-load a imágenes below the fold — imágenes que el usuario no verá hasta hacer scroll. El lazy loading de la imagen LCP añade 200–500ms al tiempo LCP.
Astro te da una base excelente (cero JS, HTML estático, CSS scoped). Pero aún puedes tener un sitio lento con imágenes no optimizadas, demasiados scripts de terceros, hojas de estilo que bloquean el render o un proveedor de hosting lento. Astro maneja el rendimiento a nivel de framework — tú manejas el rendimiento a nivel de contenido.
Significativamente. Un sitio estático en CDN (Netlify, Vercel, Cloudflare Pages) tiene un TTFB de 20–50ms globalmente. El mismo sitio en un host compartido de una sola región puede tener 200–800ms de TTFB. Para sitios estáticos de Astro: siempre usa CDN. La diferencia es a menudo 500ms+ en LCP.
Cronograma de rendimiento: Orden de optimización
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".
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.
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.
Configurar Lighthouse CI en pipeline de deployment. Monitorear datos CrUX mensualmente. Crear presupuestos de rendimiento. Detectar regresiones antes de que lleguen a los usuarios.