Picture: varios formatos y art direction
Cuándo un solo img no basta y el componente Picture construye un elemento picture con varias fuentes: negociar formatos modernos con formats y un fallbackFormat de reserva, negociar resolución con densities para la densidad de píxeles o con widths y sizes para el ancho de maqueta, y por qué la verdadera art direction, la que cambia el recorte y no solo el peso, se compone a mano con fuentes y condiciones de medio.
Del img único al elemento picture
Un <img> apunta a una imagen y a un formato. Si quieres que el navegador prefiera avif, acepte webp como segunda opción y caiga en un jpg universal si no soporta ninguno de los dos, un solo <img> se queda corto: hace falta un <picture> que envuelva varias <source>, cada una con su tipo, y termine en un <img> que sirva de red de seguridad. Escribir ese árbol a mano, con sus srcset y sus type, es tedioso y frágil. <Picture /> lo genera a partir de una lista de formatos.
---import { Picture } from 'astro:assets';import hero from '../assets/hero.png';---<Picture src={hero} formats={['avif', 'webp']} fallbackFormat="png" alt="Vista aérea del puerto al amanecer"/>Ese componente no produce un <img>, sino un <picture> con una <source> de tipo avif, otra de tipo webp y un <img> final en png. El navegador recorre las fuentes de arriba abajo y se queda con la primera cuyo tipo entiende; si no entiende ninguna, usa el <img>. Todos los demás props que ya conoces de <Image /> —alt obligatorio, width, quality— siguen valiendo aquí igual.
El HTML que genera deja ver la lógica con claridad: una fuente por formato con su srcset, y el <img> de cierre que lo sostiene todo.
<picture> <source srcset="/_astro/hero.avif" type="image/avif" /> <source srcset="/_astro/hero.webp" type="image/webp" /> <img src="/_astro/hero.png" width="1600" height="900" decoding="async" loading="lazy" alt="Vista aérea del puerto al amanecer" /></picture>Conviene no perder de vista dónde vive cada responsabilidad en ese árbol. Las <source> solo aportan candidatos: rutas y tipos entre los que el navegador elige. El <img> final es la imagen de verdad, la que se muestra y la que porta el alt, el width y el height; sin él, un <picture> no pinta nada. Por eso el fallback no es un extra opcional sino el corazón del elemento, y el texto alternativo, declarado una sola vez en el componente, acaba en ese <img> que siempre está.
formats: ofrecer varios y dejar elegir
El orden de formats es el orden de preferencia. Al poner avif antes que webp declaras: “sirve avif a quien pueda con él, y webp al resto”. Conviene listar primero el más eficiente y moderno, porque es el que ahorra más peso, y dejar los formatos más compatibles para el final de la cascada.
El fallbackFormat es la última red: el formato del <img> que se usa cuando ninguna <source> encaja. Debe ser un formato que entienda cualquier navegador —png o jpg— porque su misión es justamente no fallar nunca. Omitirlo no rompe nada, pues Astro elige uno razonable, pero declararlo deja explícito cuál es tu suelo de compatibilidad.
widths, densities y el atributo sizes
Ofrecer formatos es media negociación; la otra media es la resolución. Aquí hay dos estrategias, y elegir la correcta depende de la pregunta que quieras que el navegador responda.
densities sirve para la densidad de píxeles: cuando la imagen se muestra a un tamaño de maqueta fijo pero quieres darle el doble o el triple de píxeles a las pantallas retina. Con densities={[1.5, 2]} Astro genera un srcset con descriptores x, y cada dispositivo baja la variante que corresponde a su densidad.
<Picture src={hero} formats={['avif', 'webp']} densities={[1.5, 2]} alt="Vista aérea del puerto al amanecer"/>widths sirve para el ancho de maqueta: cuando la imagen ocupa proporciones distintas del viewport según el tamaño de pantalla —ancho completo en móvil, una columna estrecha en escritorio—. Generas varias anchuras reales y, con el atributo sizes, le dices al navegador cuánto espacio ocupará la imagen en cada caso para que elija la fuente justa. Sin sizes, widths no puede decidir bien: son una pareja inseparable.
<Picture src={hero} formats={['avif', 'webp']} widths={[240, 540, 720, 1080]} sizes="(max-width: 720px) 100vw, 720px" alt="Vista aérea del puerto al amanecer"/>No mezcles ambas: densities responde a “cuántos píxeles por punto” y widths a “cuántos puntos de ancho”, y combinarlas produce un srcset ambiguo. Elige el eje según cómo se comporte la imagen en tu maqueta.
Art direction: cuando cambia el recorte, no solo el peso
Conviene deshacer un malentendido frecuente. <Picture /> negocia formato y resolución, pero sirve la misma imagen en todas las variantes: el mismo encuadre, el mismo recorte, escalado. La art direction es otra cosa: mostrar imágenes distintas según el viewport —un plano amplio y apaisado en escritorio, un recorte vertical y cercano en móvil— porque lo que funciona en una forma no funciona en la otra.
Eso <Picture /> no lo hace solo, y es honesto saberlo. La art direction se compone a mano con un <picture> cuyas <source> llevan una condición media, cada una apuntando a una imagen ya procesada. Para generar esas fuentes procesadas sin el componente usarás getImage, la herramienta de la próxima lección; la estructura, en cambio, es este <picture> explícito.
<picture> <source media="(max-width: 640px)" srcset={movilWebp} type="image/webp" /> <source media="(min-width: 641px)" srcset={anchoWebp} type="image/webp" /> <img src={anchoFallback.src} alt="Vista aérea del puerto al amanecer" /></picture>La frontera es nítida: si solo cambia el peso o la nitidez de la misma imagen, <Picture /> te lo resuelve con formats, densities o widths. Si cambia la imagen misma según la pantalla, entras en art direction y compones el <picture> tú, con las condiciones media que tu diseño pida.