Servir la misma imagen de 2400 px a un móvil que navega por 4G es regalar tres cuartas partes del peso. Y al contrario —darle a una pantalla Retina la versión de 1x— es entregar borrosidad. HTML resuelve ambos problemas desde hace años con dos atributos que la mayoría copia sin entender: srcset y sizes. Aquí va el funcionamiento exacto.
El problema: no existe "la" imagen correcta
Una misma foto se ve en decenas de contextos: iPhone con DPR 3, portátil 1080p, monitor 4K, tablet en vertical. La imagen correcta depende de dos variables simultáneas:
- Tamaño del hueco donde se pintará (no del dispositivo: de la layout).
- Densidad de píxeles de la pantalla (device pixel ratio).
Un hueco de 400 CSS px en pantalla 2x necesita 800 píxeles físicos. El mismo hueco en 1x solo 400. Servir siempre la mayor desperdicia; servir la menor degrada. srcset + sizes comunican ambas variables al navegador, que decide solo.
Densidad: srcset con descriptor x
El modo simple lista variantes multiplicadas por densidad:
<img
src="foto-800.jpg"
srcset="foto-400.jpg 1x, foto-800.jpg 2x, foto-1200.jpg 3x"
alt="Playa al atardecer"
/>
El navegador conoce su DPR (2x en un móvil típico) y descarga foto-800.jpg. Perfecto cuando la imagen ocupa siempre el mismo tamaño en CSS px — avatares, iconos, tarjetas fijas.
Ancho fluido: srcset con w + sizes
Cuando la imagen se adapta al viewport (héroes, grids fluidos), los descriptores w declaran el ancho real en píxeles de cada fichero:
<img
src="hero-1200.jpg"
srcset="
hero-480.jpg 480w,
hero-800.jpg 800w,
hero-1200.jpg 1200w,
hero-2000.jpg 2000w
"
sizes="(max-width: 600px) 100vw, 50vw"
alt="Portada del artículo"
/>
Y aquí llega sizes, el atributo que casi nadie configura bien. Su valor NO es un estilo ni un tamaño fijo: es una adivinanza que tú le cuentas al navegador sobre cuánto ancho de viewport ocupará la imagen. La sintaxis:
sizes="[media query] tamaño, [media query] tamaño, fallback"
Leyendo el ejemplo: si el viewport mide 600 px o menos, la imagen ocupará el 100% del ancho; si es más grande, el 50%. Con esa información y la lista de srcset, el navegador calcula el mínimo necesario: viewport 1400 px, DPR 2 → necesita 1400 × 0.5 × 2 = 1400 píxeles → descarga hero-2000.jpg (el primero que supera el cálculo).
Errores típicos con sizes: olvidarlo entero (entonces el navegador asume 100vw y sobredimensiona todo), o escribir valores de diseño en vez de anchuras reales del hueco tras padding y gaps.
Art direction: picture para cambiar la foto, no solo el tamaño
srcset sirve variantes de LA MISMA proporción. Cuando quieres composición distinta —un retrato recortado en cuadrado para móvil, panorámico en escritorio— entra <picture>:
<picture>
<source media="(max-width: 600px)" srcset="retrato-480.jpg" />
<source media="(max-width: 1024px)" srcset="cuadrado-800.jpg" />
<img src="panoramica-1600.jpg" alt="Equipo trabajando" />
</picture>
El navegador evalúa las condiciones en orden y usa la primera que cumpla; el <img> final es el fallback obligatorio. También es el mecanismo para alternar formatos modernos con fallback:
<picture>
<source type="image/avif" srcset="hero.avif" />
<source type="image/webp" srcset="hero.webp" />
<img src="hero.jpg" alt="Portada" />
</picture>
Regla mental: srcset/sizes para resolución; picture para dirección de arte y formato. Se pueden combinar en el mismo bloque.
Los números del ahorro
Con una página cuyo héroe ocupa 50vw y tráfico mixto:
| Dispositivo | Sin optimizar | Con srcset+sizes |
|---|---|---|
| Móvil 4G, DPR 3, 390px | 2000w (~450 KB) | 390×0.5×3 = 585 → 800w (~60 KB) |
| Portátil DPR 1, 1440px | 2000w | 720→1200w (~180 KB) |
| Monitor 4K DPR 2, 2560px | 2000w | 2560×0.5×2=2560 → 2000w (~450 KB) |
Cada visitante recibe lo justo: el móvil baja de 450 KB a 60 KB sin pérdida visual alguna. Multiplicado por toda una página de imágenes, la diferencia entre LCP de 2 s y de 5 s está aquí — y el LCP es métrica de Core Web Vitals que Google puntúa.
Preparar las variantes
Generar 4-5 tamaños por imagen a mano es inviable; los CMS modernos lo hacen solos (next/image genera y hasta asigna sizes). Si generas variantes manualmente, conviene además comprimirlas cada una a su peso óptimo: nuestro convertidor de imágenes produce WebP/AVIF/JPG/PNG ajustando calidad, y para lotes completos el redimensionador con ZIP aplica el mismo redimensionado a decenas de ficheros de golpe. El pipeline completo de optimización está desarrollado en comprimir imágenes sin perder calidad.
Preguntas frecuentes
¿El navegador puede elegir mejor de lo que yo escriba en sizes? No: sin sizes, asume 100vw. Es tu responsabilidad describir el layout; ninguna heurística inspecciona el CSS.
¿Debo incluir lazy loading también? Sí, son complementarios: loading="lazy" difiere la descarga; srcset decide qué descargar. Combínalos salvo en la imagen LCP (que debe cargar eager y con fetchpriority alta).
¿Cuántas variantes generar? 4-6 anchuras bien elegidas cubren cualquier combinación real. Más variantes solo engordan el HTML y confunden la caché.
Genera tus variantes optimizadas con el convertidor de imágenes online, gratis y directamente en tu navegador.