Imágenes responsive en HTML
En esta lección
Una misma web se ve en un móvil de 360 píxeles y en un monitor de 2560. Si a los dos les mandas la misma foto enorme, el móvil gasta datos y tarda en cargar; si les mandas una pequeña, en el monitor se ve borrosa. Las imágenes responsive resuelven esto: le das al navegador varias versiones y él elige la mejor para cada pantalla. En esta lección verás srcset, sizes, <picture> y los atributos que hacen que las imágenes carguen rápido y sin saltos.
Lo básico de <img>, alt y <figure> está en la lección de imágenes. Aquí vamos un paso más allá.
Por qué no basta con una imagen
Mira esta línea de CSS, que ya conoces:
img { max-width: 100%; height: auto; }
Hace que la imagen se vea bien en cualquier pantalla, porque la encoge. Pero el archivo que se descarga es el mismo. Si tu foto mide 2000 píxeles y pesa 600 KB, el móvil la descarga entera para mostrarla a 360 píxeles.
Tres problemas distintos que vamos a resolver:
| Problema | Solución |
|---|---|
| Mismo archivo para pantallas grandes y pequeñas | srcset con descriptores w + sizes |
| Pantallas de alta densidad (Retina) | srcset con descriptores x |
| Recortes distintos según la pantalla, o formatos modernos | <picture> |
srcset con descriptores x: pantallas de alta densidad
Muchas pantallas tienen 2 o 3 píxeles físicos por cada píxel CSS. En ellas, un logo de 200 píxeles se ve algo borroso si el archivo mide exactamente 200.
Con los descriptores x indicas qué archivo usar según la densidad:
<img
src="logo-200.png"
srcset="logo-200.png 1x, logo-400.png 2x, logo-600.png 3x"
width="200" height="80"
alt="Club de Escalada Peña Roja">
- En una pantalla normal, el navegador descarga
logo-200.png. - En un móvil con densidad 2, descarga
logo-400.png. - El
srces la opción de reserva para navegadores muy antiguos.
Usa x cuando la imagen tiene siempre el mismo tamaño en pantalla: logos, avatares, iconos en PNG.
Consejo: si el logo es un SVG, no necesitas nada de esto. Un SVG es vectorial y se ve nítido a cualquier densidad.
srcset con descriptores w y el atributo sizes
Cuando la imagen cambia de tamaño según la pantalla (una foto de portada que ocupa todo el ancho, por ejemplo), usa descriptores w. Indican el ancho real del archivo en píxeles:
<img
src="rocodromo-800.jpg"
srcset="rocodromo-400.jpg 400w,
rocodromo-800.jpg 800w,
rocodromo-1600.jpg 1600w"
sizes="(max-width: 700px) 100vw, 700px"
width="1600" height="900"
alt="Sala de bloque con presas de colores y colchonetas azules">
Cómo decide el navegador
El navegador necesita saber cuánto va a ocupar la imagen antes de descargar el CSS. Eso se lo dice sizes:
(max-width: 700px) 100vw: si la ventana mide 700 px o menos, la imagen ocupa el 100 % del ancho de la ventana (vw= ancho de la ventana).700px: en cualquier otro caso, ocupa 700 píxeles.
Las condiciones se leen en orden y se usa la primera que se cumple. La última va sin condición: es el valor por defecto.
Con eso, el navegador hace la cuenta. En un móvil de 400 px de ancho con densidad 2:
Ancho que ocupará: 100vw = 400 px CSS
Píxeles físicos necesarios: 400 × 2 = 800
Archivo elegido: rocodromo-800.jpg
En un portátil de 1300 px con densidad 1, la imagen ocupa 700 px, así que elige rocodromo-800.jpg (el más pequeño que llega a 700).
Cuidado: si usas descriptores
wy olvidassizes, el navegador supone100vwy puede descargar la versión más grande aunque la imagen se muestre pequeña.
¿Cuántas versiones hago?
Tres o cuatro suelen bastar: por ejemplo, 400, 800, 1200 y 1600 píxeles de ancho. Muchas herramientas (Squoosh, el propio framework que uses, un CDN de imágenes) las generan automáticamente.
<picture>: dirección de arte y formatos
<picture> es un contenedor con varios <source> y un <img> final obligatorio. El navegador recorre los <source> en orden y usa el primero que encaja. Si ninguno encaja, usa el <img>.
<source media>: recortes distintos por pantalla
A veces no quieres la misma foto más pequeña, sino otra foto: en el móvil, un recorte vertical centrado en la escaladora; en el ordenador, la panorámica completa del muro. A esto se le llama dirección de arte:
<picture>
<source media="(max-width: 600px)" srcset="portada-vertical.jpg">
<source media="(max-width: 1100px)" srcset="portada-media.jpg">
<img src="portada-panoramica.jpg" width="1600" height="600"
alt="Escaladora colgada del techo del desplome">
</picture>
Aquí media es una orden: si la condición se cumple, se usa ese archivo sí o sí.
<source type>: AVIF y WebP con alternativa
Los formatos modernos pesan mucho menos que JPG. Con type ofreces el formato moderno y dejas JPG para quien no lo entienda:
<picture>
<source srcset="bloque.avif" type="image/avif">
<source srcset="bloque.webp" type="image/webp">
<img src="bloque.jpg" width="1200" height="800" loading="lazy"
alt="Dos personas escalando un bloque amarillo">
</picture>
Hoy todos los navegadores actuales entienden WebP y AVIF, pero mantener el JPG de reserva no cuesta nada.
Puntos clave de <picture>
- El
alt,width,heightyloadingvan en el<img>, no en los<source>. - Los
<source>pueden llevar su propiosrcsetcon descriptores ysizes. - El estilo CSS se aplica al
<img>, no al<picture>.
Cargar rápido y sin saltos
width y height: adiós a los saltos (CLS)
¿Te ha pasado que vas a pulsar un botón y, justo entonces, carga una imagen, todo baja y pulsas otra cosa? Ese salto se llama CLS (Cumulative Layout Shift) y Google lo mide como parte de sus Core Web Vitals.
Se evita poniendo width y height con las proporciones del archivo:
<img src="foto.jpg" width="1200" height="800" alt="...">
Con eso, el navegador calcula la proporción (3:2) y reserva el hueco antes de descargar nada. Junto con max-width: 100%; height: auto; en CSS, la imagen se adapta al ancho y mantiene su hueco.
loading="lazy": carga diferida
<img src="galeria-7.jpg" width="600" height="400" loading="lazy" alt="...">
La imagen solo se descarga cuando el usuario se acerca a ella al hacer scroll. Ahorra datos y acelera la carga inicial.
Cuidado: no pongas
loading="lazy"en la imagen principal que se ve nada más abrir la página (la de portada). La retrasarías, y es justo la que más prisa corre.
fetchpriority: dar prioridad a lo importante
Para esa imagen principal, haz lo contrario: pide que se descargue antes que otras cosas.
<img src="portada.jpg" width="1600" height="600" fetchpriority="high" alt="...">
Admite high, low y auto (por defecto). Úsalo en una sola imagen por página: si todo es prioritario, nada lo es.
decoding="async"
Permite que el navegador decodifique la imagen (convertir el archivo en píxeles) sin bloquear el resto de la página. Es una mejora pequeña, útil en galerías con muchas imágenes. Los valores son async, sync y auto.
Qué formato usar en cada caso
| Formato | Tipo | Transparencia | Úsalo para |
|---|---|---|---|
| JPG | Mapa de bits, con pérdida | No | Fotos, como formato de reserva |
| PNG | Mapa de bits, sin pérdida | Sí | Capturas de pantalla, gráficos con texto, imágenes con transparencia |
| WebP | Mapa de bits, con o sin pérdida | Sí | Casi todo: fotos y gráficos, pesa bastante menos que JPG y PNG |
| AVIF | Mapa de bits, con o sin pérdida | Sí | Fotos: suele ser el más ligero de todos |
| SVG | Vectorial | Sí | Logos, iconos, ilustraciones sencillas |
| GIF | Mapa de bits, 256 colores | Sí (sin bordes suaves) | Casi nada: para animaciones es mejor un vídeo o un WebP animado |
Una regla práctica: fotos en AVIF o WebP con JPG de reserva; logos e iconos en SVG; capturas en PNG o WebP.
Sobre SVG tienes una lección entera: gráficos con SVG y canvas.
Errores frecuentes
- Usar descriptores
wsinsizes. El navegador supone que la imagen ocupa toda la pantalla y descarga la versión grande. - Mezclar
xywen el mismosrcset. No está permitido: elige uno. - Poner el
alten el<source>. Va en el<img>. - Olvidar el
<img>dentro de<picture>. Sin él no se muestra nada. loading="lazy"en la imagen de portada. Empeora la carga en lugar de mejorarla.- Omitir
widthyheight. La página salta al cargar las imágenes. - Poner en
widthyheightun tamaño con otra proporción que la del archivo: la imagen se deforma.
Resumen
| Herramienta | Qué resuelve |
|---|---|
srcset con 1x, 2x | Imágenes de tamaño fijo en pantallas de alta densidad |
srcset con 400w, 800w + sizes | Imágenes que cambian de tamaño según la pantalla |
<picture> + <source media> | Un recorte distinto para cada tamaño de pantalla |
<picture> + <source type> | Formatos modernos (AVIF, WebP) con JPG de reserva |
width y height | Reservar el hueco y evitar saltos (CLS) |
loading="lazy" | Retrasar las imágenes que están más abajo |
fetchpriority="high" | Adelantar la imagen principal |
decoding="async" | Decodificar sin bloquear la página |
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.