Responsive Bilder: srcset, sizes und moderne Formate
Meistere die Auslieferung responsiver Bilder mit srcset, sizes, dem picture-Element und Formaten wie AVIF und WebP — schneller, ohne Qualitätsverlust.

Bilder machen auf den meisten Webseiten den Großteil der übertragenen Bytes aus. Ein 2400px großes Hero-Bild an einen 375px breiten mobilen Bildschirm auszuliefern verschwendet Bandbreite, drückt die Performance-Werte nach unten und frustriert Nutzer mit langsameren Verbindungen. Trotzdem liefern die meisten Entwickler weiterhin nur eine einzige Bildgröße aus und betrachten die Aufgabe als erledigt.
Responsive Bilder lösen dieses Problem, indem sie den Browser anhand von Viewport-Breite, Pixeldichte und unterstützten Formaten die optimale Bildvariante auswählen lassen. HTML bringt die nötigen Werkzeuge bereits mit—srcset, sizes und das <picture>-Element. Die Herausforderung besteht darin, zu verstehen, wie sie zusammenspielen.
Das Problem mit Bildern aus einer einzigen Quelle
Ein einzelnes <img>-Tag zwingt jedes Gerät dazu, dieselbe Datei herunterzuladen, unabhängig von Bildschirmgröße oder Leistungsfähigkeit.
<!-- ❌ One size for all devices -->
<img
src="/images/hero-2400.jpg"
alt="Mountain landscape at sunrise"
width="2400"
height="1600"
/>
<!--
Mobile user on 3G: downloads 800KB for a 375px screen
Desktop user on fiber: gets the right size by accident
Neither gets modern formats like AVIF or WebP
--><!-- ✅ Responsive with srcset and sizes -->
<img
src="/images/hero-800.jpg"
srcset="
/images/hero-400.jpg 400w,
/images/hero-800.jpg 800w,
/images/hero-1200.jpg 1200w,
/images/hero-1600.jpg 1600w,
/images/hero-2400.jpg 2400w
"
sizes="
(max-width: 640px) 100vw,
(max-width: 1024px) 80vw,
1200px
"
alt="Mountain landscape at sunrise"
width="2400"
height="1600"
loading="lazy"
decoding="async"
/>Das Attribut srcset teilt dem Browser mit, welche Bildbreiten verfügbar sind. Das Attribut sizes teilt dem Browser mit, wie breit das Bild bei jedem Breakpoint dargestellt wird. Der Browser kombiniert diese Informationen mit dem Device Pixel Ratio, um die optimale Datei auszuwählen.
srcset und sizes verstehen
Der w-Deskriptor in srcset gibt die intrinsische Breite jeder Bilddatei an. Der Browser nutzt sizes, um die dargestellte Breite zu bestimmen, und wählt dann die beste Übereinstimmung.
<!--
How the browser decides:
Device: iPhone 14 (390px viewport, 3x DPR)
sizes says: at max-width 640px → 100vw → 390px
Effective pixels needed: 390 × 3 = 1170px
Browser picks: hero-1200.jpg (closest match ≥ 1170)
Device: MacBook Pro (1440px viewport, 2x DPR)
sizes says: at max-width 1024px? No.
Falls through to default: 1200px
Effective pixels needed: 1200 × 2 = 2400px
Browser picks: hero-2400.jpg
-->
<img
src="/images/hero-800.jpg"
srcset="
/images/hero-400.jpg 400w,
/images/hero-800.jpg 800w,
/images/hero-1200.jpg 1200w,
/images/hero-1600.jpg 1600w,
/images/hero-2400.jpg 2400w
"
sizes="
(max-width: 640px) 100vw,
(max-width: 1024px) 80vw,
1200px
"
alt="Dashboard analytics view"
width="2400"
height="1600"
/>Ein häufiger Fehler: sizes immer auf 100vw zu setzen. Auf dem Desktop erreichen Bilder innerhalb eines Containers nur selten die volle Viewport-Breite. Ungenaue sizes-Werte führen dazu, dass der Browser zu große Bilder herunterlädt.
<!-- ❌ sizes always 100vw wastes bandwidth on desktop -->
<img
srcset="
/images/card-400.jpg 400w,
/images/card-800.jpg 800w
"
sizes="100vw"
alt="Product card"
/>
<!-- ✅ Accurate sizes match actual layout -->
<img
srcset="
/images/card-400.jpg 400w,
/images/card-800.jpg 800w
"
sizes="
(max-width: 640px) 100vw,
(max-width: 1024px) 50vw,
33vw
"
alt="Product card"
/>Das picture-Element für die Formataushandlung
Während srcset das Umschalten der Auflösung übernimmt, kümmert sich <picture> um die Formataushandlung und die Art Direction. Der Browser wählt das erste <source>-Element, das er unterstützt.
<picture>
<!-- AVIF: smallest file, newest format -->
<source
type="image/avif"
srcset="
/images/hero-400.avif 400w,
/images/hero-800.avif 800w,
/images/hero-1200.avif 1200w,
/images/hero-1600.avif 1600w
"
sizes="(max-width: 640px) 100vw, 1200px"
/>
<!-- WebP: good compression, wide support -->
<source
type="image/webp"
srcset="
/images/hero-400.webp 400w,
/images/hero-800.webp 800w,
/images/hero-1200.webp 1200w,
/images/hero-1600.webp 1600w
"
sizes="(max-width: 640px) 100vw, 1200px"
/>
<!-- JPEG fallback: universal support -->
<img
src="/images/hero-800.jpg"
srcset="
/images/hero-400.jpg 400w,
/images/hero-800.jpg 800w,
/images/hero-1200.jpg 1200w,
/images/hero-1600.jpg 1600w
"
sizes="(max-width: 640px) 100vw, 1200px"
alt="Mountain landscape at sunrise"
width="1600"
height="1067"
loading="lazy"
decoding="async"
/>
</picture>AVIF erzielt bei vergleichbarer visueller Qualität in der Regel 50% kleinere Dateien als JPEG. WebP liegt zwischen den beiden. Indem AVIF zuerst aufgeführt wird, erhalten Browser, die es unterstützen, die kleinsten Dateien, während ältere Browser auf WebP oder JPEG zurückfallen.
Art Direction mit Media Queries
Manchmal benötigen unterschiedliche Viewports grundlegend andere Bildausschnitte, nicht nur andere Auflösungen. Ein breites Panorama-Hero-Bild auf dem Desktop braucht auf dem Handy womöglich einen quadratischen Ausschnitt, damit das Motiv sichtbar bleibt.
<picture>
<!-- Mobile: square crop focused on subject -->
<source
media="(max-width: 640px)"
srcset="
/images/hero-mobile-400.avif 400w,
/images/hero-mobile-800.avif 800w
"
sizes="100vw"
type="image/avif"
/>
<source
media="(max-width: 640px)"
srcset="
/images/hero-mobile-400.webp 400w,
/images/hero-mobile-800.webp 800w
"
sizes="100vw"
type="image/webp"
/>
<!-- Desktop: wide landscape -->
<source
srcset="
/images/hero-desktop-1200.avif 1200w,
/images/hero-desktop-1600.avif 1600w,
/images/hero-desktop-2400.avif 2400w
"
sizes="100vw"
type="image/avif"
/>
<img
src="/images/hero-desktop-1200.jpg"
srcset="
/images/hero-desktop-1200.jpg 1200w,
/images/hero-desktop-1600.jpg 1600w,
/images/hero-desktop-2400.jpg 2400w
"
sizes="100vw"
alt="Team collaborating in an open office"
width="2400"
height="800"
/>
</picture>Bildgenerierung mit Sharp automatisieren
Mehrere Größen und Formate manuell zu erstellen ist auf Dauer nicht praktikabel. Automatisieren Sie den Vorgang mit Sharp in einem Build-Skript.
import sharp from "sharp";
import { readdir, mkdir } from "node:fs/promises";
import { join, parse } from "node:path";
const INPUT_DIR = "images/originals";
const OUTPUT_DIR = "public/images";
const WIDTHS = [400, 800, 1200, 1600, 2400];
const FORMATS = ["avif", "webp", "jpg"] as const;
type ImageFormat = (typeof FORMATS)[number];
const FORMAT_OPTIONS: Record<ImageFormat, object> = {
avif: { quality: 60, effort: 6 },
webp: { quality: 75, effort: 5 },
jpg: { quality: 80, mozjpeg: true },
};
async function processImage(
inputPath: string
): Promise<void> {
const { name } = parse(inputPath);
const image = sharp(inputPath);
const metadata = await image.metadata();
const originalWidth = metadata.width ?? 2400;
for (const format of FORMATS) {
for (const width of WIDTHS) {
if (width > originalWidth) continue;
const outputPath = join(
OUTPUT_DIR,
`${name}-${width}.${format}`
);
await sharp(inputPath)
.resize(width, null, {
withoutEnlargement: true,
fit: "inside",
})
.toFormat(format, FORMAT_OPTIONS[format])
.toFile(outputPath);
console.log(` ✅ ${name}-${width}.${format}`);
}
}
}
async function main() {
await mkdir(OUTPUT_DIR, { recursive: true });
const files = await readdir(INPUT_DIR);
const images = files.filter((f) =>
/\.(jpg|jpeg|png|tiff)$/i.test(f)
);
console.log(
`Processing ${images.length} images...\n`
);
for (const file of images) {
console.log(`📸 ${file}`);
await processImage(join(INPUT_DIR, file));
}
console.log("\n✅ All images processed");
}
main();Umsetzung in React/Next.js
Moderne Frameworks abstrahieren responsive Bilder oft, aber das Verständnis des zugrunde liegenden HTML hilft, wenn diese Abstraktion an ihre Grenzen stößt.
// ResponsiveImage.tsx
interface ImageProps {
src: string; // base name without extension
alt: string;
width: number;
height: number;
sizes: string;
priority?: boolean;
className?: string;
}
const WIDTHS = [400, 800, 1200, 1600, 2400];
function buildSrcSet(
base: string,
format: string
): string {
return WIDTHS.map(
(w) => `/images/${base}-${w}.${format} ${w}w`
).join(", ");
}
export function ResponsiveImage({
src,
alt,
width,
height,
sizes,
priority = false,
className,
}: ImageProps) {
return (
<picture>
<source
type="image/avif"
srcSet={buildSrcSet(src, "avif")}
sizes={sizes}
/>
<source
type="image/webp"
srcSet={buildSrcSet(src, "webp")}
sizes={sizes}
/>
<img
src={`/images/${src}-800.jpg`}
srcSet={buildSrcSet(src, "jpg")}
sizes={sizes}
alt={alt}
width={width}
height={height}
loading={priority ? "eager" : "lazy"}
decoding={priority ? "sync" : "async"}
fetchPriority={priority ? "high" : "auto"}
className={className}
/>
</picture>
);
}
// Usage
<ResponsiveImage
src="hero-landscape"
alt="Mountain landscape at sunrise"
width={2400}
height={1600}
sizes="(max-width: 640px) 100vw, 1200px"
priority
/>Das Wichtigste in Kürze
Verwenden Sie srcset mit Breiten-Deskriptoren (w), um mehrere Bildauflösungen bereitzustellen, und sizes, um dem Browser die dargestellte Breite bei jedem Breakpoint mitzuteilen—präzise sizes-Werte sind entscheidend, weil der Browser sie nutzt, um vor dem Layout zu berechnen, welches Bild heruntergeladen werden soll. Das <picture>-Element übernimmt die Formataushandlung, indem es Quellen in Prioritätsreihenfolge auflistet (zuerst AVIF, dann WebP, dann JPEG als Fallback), sodass jeder Browser das effizienteste von ihm unterstützte Format herunterladen kann. Art Direction erfordert separate <source>-Elemente mit media-Attributen, wenn unterschiedliche Viewports unterschiedliche Ausschnitte benötigen—die Auflösungsumschaltung mit srcset skaliert nur dasselbe Bild, sie verändert nicht die Bildkomposition. Automatisieren Sie die Bildgenerierung mit Werkzeugen wie Sharp, um während des Builds alle Größen- und Formatvarianten aus den Quelldateien zu erzeugen—das erspart die manuelle Bildbearbeitung und sorgt für ein konsistentes Ergebnis bei jedem Bild im Projekt.


