WebP puede reducir el archivo, pero no elige por sí solo cuántos píxeles descarga el navegador. Si la conversión solo cambia el formato, una imagen de 1.200 píxeles seguirá con 1.200 píxeles, incluso cuando aparezca con la mitad de ese ancho en la pantalla.

Este blog encontró el problema en la prueba automatizada de rendimiento. El 19 de julio de 2026, la imagen del destacado de la portada pasó de un motivo gráfico de cerca de 8 KB a una imagen de aproximadamente 50 KB. En el entorno de integración continua, el Largest Contentful Paint (LCP), el tiempo hasta la renderización del mayor elemento de contenido elegible en el área visible, llegó a 2.723 ms y superó el techo de 2.500 ms.

La corrección no fue degradar la imagen. El generador pasó a producir versiones de 1.200 y 800 píxeles, mientras el HTML ganó srcset y sizes. En una comparación local separada, antes y después en el mismo perfil móvil simulado, el LCP bajó de 2.793 a 2.122 ms. El formato siguió siendo WebP; fue el navegador el que pasó a recibir una alternativa compatible con el espacio ocupado.

El caso original ocurrió con Astro 7.1.1. En la versión en la que se escribió este texto, la implementación se volvió a revisar con Astro 7.2.2 y Node.js 24.15.0; hoy el proyecto usa Astro 7.3.5, y la versión mínima declarada sigue siendo Node.js 22.12.0. srcset y sizes, sin embargo, forman parte del HTML y no dependen de Astro para funcionar.

WebP cambia el formato, no elige el tamaño

Convertir una imagen a WebP puede reducir el archivo, pero no crea automáticamente versiones para distintas pantallas. Si el HTML ofrece solo este recurso, el navegador no tiene alternativa:

<img
  src="/images/blog/exemplo/card.webp"
  alt=""
  width="1200"
  height="630"
/>

En este ejemplo didáctico, el CSS puede renderizar la imagen con 360 píxeles de ancho. La descarga, sin embargo, sigue siendo el archivo de 1.200 píxeles indicado en src.

El formato y la resolución resuelven problemas distintos. WebP comprime cada candidato. La imagen responsiva crea candidatos más pequeños e informa en qué contexto puede ser útil cada uno.

La cuenta que orienta la elección

Cuando usa descriptores de ancho (w), el atributo srcset presenta los archivos disponibles y el ancho natural de cada uno. El atributo sizes describe el ancho que la imagen debe ocupar en el diseño, también llamado espacio de visualización.

<img
  src="/images/blog/exemplo/card.webp"
  srcset="
    /images/blog/exemplo/card-800.webp 800w,
    /images/blog/exemplo/card.webp 1200w
  "
  sizes="(min-width: 768px) 720px, calc(100vw - 2rem)"
  alt=""
  width="1200"
  height="630"
/>

El descriptor 800w no redimensiona el archivo. Declara que aquel recurso tiene 800 píxeles de ancho natural. El valor tiene que corresponder al archivo real.

Con esa información, el navegador calcula la densidad efectiva de cada candidato: el ancho del archivo dividido por el ancho previsto en sizes. La elección también puede considerar la densidad de la pantalla, el zoom y las condiciones de red, y el resultado final depende de la implementación del navegador. Por eso, srcset ofrece opciones; no es una orden rígida de descargar siempre el archivo más pequeño.

En el caso del 19 de julio, el sizes móvil era 100vw. El perfil usado por la compuerta tenía 412 píxeles CSS de ancho y una relación de píxeles del dispositivo de 1,75. Para definir la variante más pequeña, el proyecto tomó el ancho entero de la ventana como referencia conservadora: 412 × 1,75, cerca de 721 píxeles nominales. Una versión de 720 píxeles quedaría ligeramente por debajo de ese objetivo. La de 800 píxeles ofreció margen y siguió más cerca que la de 1.200.

El detalle importa porque la densidad transforma una tarjeta aparentemente pequeña en una necesidad mayor de resolución. Medir solo el ancho CSS ignora ese factor.

Las variantes de 800 píxeles ocupaban un 54,6% menos

El generador de portadas de este blog usa Sharp para producir card.webp, de 1.200 × 630 píxeles, y card-800.webp, de 800 × 420. Esta representación abreviada muestra solo la transformación de la variante responsiva:

const card800 = await sharp(variantSource)
  .resize(800, 420, { fit: 'cover', position: 'centre' })
  .webp({ quality: 75, effort: 6 })
  .toBuffer();

Antes de la inclusión de las imágenes de este post, el 21 de agosto de 2026, los 17 pares publicados entonces sumaban:

Las variantes de 800 píxeles ocupaban un 54,6% menos
VarianteDimensiónTotal en la colección
card.webp1.200 × 630939.640 bytes
card-800.webp800 × 420426.294 bytes

Las variantes de 800 píxeles ocupaban, sumadas, un 54,6% menos de bytes. La diferencia combina menos píxeles con la configuración de compresión de la variante, que usa calidad 75; no es un experimento aislado de resolución. Eso mide los archivos disponibles en el repositorio, no un ahorro garantizado en cada visita. La ganancia de red solo ocurre cuando el navegador selecciona el candidato más pequeño, y la compresibilidad cambia según la imagen.

El mismo archivo ocupa espacios distintos

Crear dos imágenes fue solo la mitad de la corrección. La portada usa imágenes de la misma colección en tres tipos de región con anchos distintos: destacado, grilla de posts recientes y archivo.

El mismo archivo ocupa espacios distintos
RegiónValor de sizes
Destacado(min-width: 1200px) 1072px, (min-width: 768px) calc(100vw - 128px), (min-width: 640px) calc(100vw - 96px), calc(100vw - 60px)
Grilla reciente(min-width: 1200px) 520px, (min-width: 960px) calc(50vw - 80px), (min-width: 768px) calc(100vw - 128px), (min-width: 640px) calc(100vw - 96px), calc(100vw - 60px)
Archivo(min-width: 640px) 208px, min(208px, calc(100vw - 60px))

El srcset puede ser igual en las tres regiones. El sizes no, porque declara una estimación del espacio que ocupa cada componente. Informar 100vw para una tarjeta que ocupa solo una fracción de la ventana hace que el navegador sobrestime la necesidad y tiende a favorecer un archivo más grande.

En forma equivalente y abreviada, la configuración del destacado quedó así:

<img
  src={featuredCardImage}
  srcset={cardSrcset(featuredCardImage)}
  sizes="(min-width: 1200px) 1072px, (min-width: 768px) calc(100vw - 128px), (min-width: 640px) calc(100vw - 96px), calc(100vw - 60px)"
  alt=""
  loading="eager"
  fetchpriority="high"
  decoding="async"
  width="1200"
  height="630"
/>

El sizes no controla el ancho visual; el CSS sigue siendo responsable del diseño. width y height establecen la proporción intrínseca y permiten reservar espacio antes de la carga, lo que ayuda a evitar el desplazamiento de contenido.

El texto alternativo está vacío en este caso porque la portada es decorativa y el titular aparece junto a ella en texto real. Una imagen que comunica información propia necesita una descripción adecuada, no un alt="" copiado de este ejemplo.

La imagen del LCP no puede esperar

En la portada, solo la imagen destacada recibe loading="eager" y fetchpriority="high". Esos atributos ya existían antes de la corrección de este caso; no fueron ellos los que recuperaron la métrica. Permanecen porque la imagen es la candidata probable a LCP y necesita descubrirse pronto. Las portadas debajo de ella usan loading="lazy", porque pueden esperar hasta acercarse al área visible.

Aplicar la carga diferida a la imagen de LCP crea un retraso innecesario. Hacer lo contrario y marcar todas las portadas con prioridad alta también perjudica la jerarquía: si todo es urgente, la indicación deja de ayudar al navegador a decidir.

Esa separación completa la solución. srcset y sizes se ocupan del recurso adecuado; loading y fetchpriority se ocupan del momento y de la prioridad. El presupuesto de Core Web Vitals de este blog sigue siendo la compuerta que confirma si la combinación funcionó.

Cuándo dejar que Astro genere las variantes

Astro puede automatizar buena parte de ese trabajo cuando la imagen está en src/ y se importa mediante astro:assets. La propiedad layout genera srcset y sizes:

---
import { Image } from 'astro:assets';
import capa from '../assets/capa.webp';
---

<Image
  src={capa}
  alt="Descripción del contenido visual"
  layout="constrained"
  width={1200}
  height={630}
/>

El redimensionamiento visual todavía necesita los estilos responsivos de Astro, habilitados por image.responsiveStyles: true, o de CSS equivalente en el proyecto.

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  image: { responsiveStyles: true },
});

Las portadas de este proyecto ya llegan procesadas a public/images/blog/, por eso el flujo manual es necesario. Astro copia archivos de public/ sin transformación y no crea versiones responsivas para ellos.

La elección, por lo tanto, depende del origen de la imagen. Un archivo importado de src/ puede usar el servicio de Astro. Si permanece en este flujo en public/, el archivo producido por un generador externo tiene que llegar con las variantes listas y recibir el HTML correspondiente.

Cómo verificar sin confiar en el ojo

Una página puede parecer correcta mientras transfiere el archivo equivocado. La verificación tiene que observar la decisión del navegador.

  1. Ejecute npm run build y después npm run preview para abrir la página en un servidor de previsualización.
  2. Active el perfil móvil antes de recargar, deshabilite la caché en la pestaña de red y haga una carga nueva.
  3. Inspeccione la propiedad currentSrc, que muestra qué candidato se seleccionó.
const imagem = document.querySelector('.home-featured img');

({
  arquivo: imagem?.currentSrc,
  larguraExibida: imagem?.getBoundingClientRect().width,
  larguraNatural: imagem?.naturalWidth,
  densidade: window.devicePixelRatio,
});
  1. Confirme en la pestaña de red el archivo transferido y su tamaño. Redimensionar una pestaña ya cargada puede reutilizar el candidato más grande de la caché; por eso la prueba empieza con el perfil elegido y una carga nueva.
  2. Ejecute la medición de rendimiento en la página que más concentra imágenes. En este proyecto es npm run test:lighthouse, con atención primero a la portada.

El resultado esperado no es “800 píxeles en cualquier celular”. Es un candidato coherente con el espacio, la densidad y las condiciones que el navegador evaluó, sin sacrificar nitidez ni transferir 1.200 píxeles por falta de alternativa.

Los errores que hacen que WebP siga pesado

  • Ofrecer variantes sin un sizes fiel. El navegador conoce los archivos, pero recibe una descripción equivocada del espacio.
  • Usar un descriptor que no corresponde al archivo. 800w tiene que significar 800 píxeles naturales.
  • Aplicar loading="lazy" al LCP probable. El ahorro de prioridad se vuelve retraso visible.
  • Marcar todas las imágenes como prioridad alta. La página pierde la diferenciación entre destacado y contenido secundario.
  • Probar solo redimensionando una pestaña con caché. El recurso ya elegido puede enmascarar la configuración nueva.
  • Copiar 800 y 1.200 como números universales. Los candidatos tienen que nacer del diseño, de las densidades relevantes y de las imágenes del proyecto.

Las mediciones de LCP de este caso vinieron de un perfil de laboratorio fijo, no de una comparación de usuarios reales. Otra portada, otra imagen u otra distribución de dispositivos puede exigir tres o más anchos. Si la composición de la imagen también tiene que cambiar en el celular, el problema pasa a ser de dirección de arte, normalmente implementada en HTML con <picture>.

WebP puede compactar cada candidato. srcset ofrece las alternativas. sizes describe el espacio. La medición muestra si el navegador recibió información suficiente para elegir bien.

Fuentes