Saltar al contenido
imagenes-responsive.html · devschool

Imágenes responsive en HTML

Lección 11 de 23 · 8 min de lectura · Actualizado el

En esta lección
  1. Por qué no basta con una imagen
  2. srcset con descriptores x: pantallas de alta densidad
  3. srcset con descriptores w y el atributo sizes
  4. <picture>: dirección de arte y formatos
  5. Cargar rápido y sin saltos
  6. Qué formato usar en cada caso
  7. Errores frecuentes
  8. Resumen

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:

ProblemaSolución
Mismo archivo para pantallas grandes y pequeñassrcset 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 src es 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 w y olvidas sizes, el navegador supone 100vw y 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, height y loading van en el <img>, no en los <source>.
  • Los <source> pueden llevar su propio srcset con descriptores y sizes.
  • 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

FormatoTipoTransparenciaÚsalo para
JPGMapa de bits, con pérdidaNoFotos, como formato de reserva
PNGMapa de bits, sin pérdidaSíCapturas de pantalla, gráficos con texto, imágenes con transparencia
WebPMapa de bits, con o sin pérdidaSíCasi todo: fotos y gráficos, pesa bastante menos que JPG y PNG
AVIFMapa de bits, con o sin pérdidaSíFotos: suele ser el más ligero de todos
SVGVectorialSíLogos, iconos, ilustraciones sencillas
GIFMapa de bits, 256 coloresSí (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 w sin sizes. El navegador supone que la imagen ocupa toda la pantalla y descarga la versión grande.
  • Mezclar x y w en el mismo srcset. No está permitido: elige uno.
  • Poner el alt en 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 width y height. La página salta al cargar las imágenes.
  • Poner en width y height un tamaño con otra proporción que la del archivo: la imagen se deforma.

Resumen

HerramientaQué resuelve
srcset con 1x, 2xImágenes de tamaño fijo en pantallas de alta densidad
srcset con 400w, 800w + sizesImá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 heightReservar 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).

imagenes-responsive.html
Resultado

Pon a prueba lo que has aprendido

[HTML] ¿Qué le dice al navegador el atributo sizes en este código?
<img src="ruta-800.jpg"
     srcset="ruta-400.jpg 400w, ruta-800.jpg 800w, ruta-1600.jpg 1600w"
     sizes="(max-width: 600px) 100vw, 50vw"
     alt="Vista del refugio">

[HTML] ¿Cuándo usarías <picture> en lugar de un simple <img> con srcset?

[HTML] ¿Qué error hay en poner loading="lazy" en la foto grande de la cabecera?

[HTML] Dentro de <picture>, ¿qué elemento es obligatorio y además muestra la imagen?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.