This episode covers responsive images: the srcset and sizes attributes for choosing the right size, the picture element for format and art direction, and object-fit and object-position for cropping images inside a predefined frame.

Images are often the biggest contributor to a page's weight. Loading a 2000px image to display in a 300px column wastes bandwidth and slows down load time. Responsive images solve this by choosing the right image source based on device conditions.
On the visual side, there's another problem: frames that don't match an image's aspect ratio. That's where object-fit comes in — controlling how an image is fitted into a box without distortion.
Why does it matter? A fast page is a user-friendly page that ranks better. Mastering srcset, picture, and object-fit lets you load just enough image and display it correctly at every screen ratio.
srcset gives the browser a list of images with their intrinsic widths. The browser picks the most suitable one based on the viewport and pixel density factor:
<img
src="kucing-800.jpg"
srcset="kucing-400.jpg 400w, kucing-800.jpg 800w, kucing-1600.jpg 1600w"
sizes="(min-width: 900px) 50vw, 100vw"
alt="Kucing sedang tidur"
width="800"
height="600"
>The sizes attribute tells the browser how much space the image will occupy under certain conditions. These rules require the width and height attributes so the browser can calculate space before the image loads — preventing layout jumps when the image arrives.
<img
src="logo.png"
srcset="logo.png 1x, logo@2x.png 2x"
alt="Logo DevNull"
>The 1x and 2x notations pick the image according to the device pixel ratio. On Retina screens (ratio 2), the browser takes logo@2x.png so the image stays sharp.
srcset only picks a larger or smaller version of the same image. When the image needs to be cropped differently at certain screens — art direction — use the picture element:
<picture>
<source
srcset="gambar-lanskap.jpg"
media="(min-width: 900px)"
>
<source
srcset="gambar-potret.jpg"
media="(min-width: 480px)"
>
<img
src="gambar-potret.jpg"
alt="Pemandangan gunung"
loading="lazy"
>
</picture>The browser uses the first source whose media query matches. The final img element acts as the fallback as well as the source of the alt text and other attributes. source can also pick modern formats like WebP or AVIF:
<picture>
<source srcset="gambar.avif" type="image/avif">
<source srcset="gambar.webp" type="image/webp">
<img src="gambar.jpg" alt="Ilustrasi" loading="lazy">
</picture>Browsers that support AVIF load it; others fall back to WebP, and finally to JPEG. Newer formats are generally much smaller with similar visual quality.
An img element given width and height will still be distorted when the aspect ratio doesn't match. object-fit changes this behavior:
.gambar-cover {
width: 100%;
height: 240px;
object-fit: cover;
}
.gambar-contain {
width: 100%;
height: 240px;
object-fit: contain;
object-position: center;
}object-fit: cover crops the image to fill the frame without distortion — the common choice for thumbnails and heroes. contain fits the entire image into the frame, leaving empty space when the ratio doesn't match.
.avatar {
width: 96px;
height: 96px;
border-radius: 50%;
object-fit: cover;
object-position: top;
}object-position shifts which part of the image is visible. top shows the top of the image — important for face photos that usually get cropped when centered.
Combining aspect-ratio and object-fit produces a stable image frame:
.thumbnail {
aspect-ratio: 16 / 9;
width: 100%;
object-fit: cover;
display: block;
}The aspect-ratio property sets the frame's ratio regardless of the image's intrinsic size. Combining it with object-fit: cover and the width/height attributes in HTML keeps placeholders, cards, and galleries consistent and free of layout shifts.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kartu Gambar</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<article class="kartu">
<img
class="gambar"
src="https://picsum.photos/seed/devnull/800/450"
srcset="https://picsum.photos/seed/devnull/400/225 400w,
https://picsum.photos/seed/devnull/800/450 800w"
sizes="(min-width: 640px) 50vw, 100vw"
alt="Gambar artikel"
width="800"
height="450"
loading="lazy"
>
<div class="isi">
<h2>Judul Artikel</h2>
<p>Deskripsi singkat artikel yang ditampilkan di kartu.</p>
</div>
</article>
</body>
</html>* { box-sizing: border-box; }
body { margin: 0; padding: 24px; font-family: system-ui, sans-serif; background-color: #f1f5f9; }
.kartu { max-width: 640px; margin-inline: auto; background-color: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 12px rgba(15, 23, 42, 0.1); }
.gambar { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.isi { padding: 24px; }Run npx serve ., then open the Network tab in DevTools. As the viewport narrows, the browser should load the smaller 400w version. This source selection happens in the browser, so you don't need to write manual logic.
If an image looks stretched or squashed, check whether height is set alongside width without object-fit. Add object-fit: cover and define the ratio you want.
Without sizes, the browser assumes the image is as wide as the full viewport and tends to pick the largest version. Always include an accurate sizes so image selection stays efficient.
An img without width and height doesn't reserve space before loading, causing layout shift. Include the intrinsic dimensions on every image.
Key takeaways:
srcset with widths and sizes picks the right image for the viewport.1x and 2x notations adapt to screen pixel density.picture element enables art direction and modern format selection.object-fit: cover crops without distortion; contain shows the whole image.object-position shifts the visible part of the image.aspect-ratio and dimension attributes prevent layout shifts.
In the next episode, episode 12, we'll cover mobile-first design and device adaptation — designing for phones first and handling notches, safe areas, and touch targets.