Responsive Images
Responsive images use the srcset and sizes attributes, plus the <picture> element, to let the browser pick the best image file for each device's screen size, resolution, and supported formats, saving bandwidth without sacrificing sharpness.
Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.
Part of the free HTML & CSS course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll serve every visitor an image sized for their screen, swap crops and modern formats with picture , lazy-load what's off-screen, and reserve space so your page never jumps while images load.
Responsive images are like a coffee shop offering small, medium and large cups. You wouldn't pour a venti into someone who asked for an espresso — you'd waste coffee and they couldn't drink it all. A 4000-pixel photo sent to a phone is exactly that waste: megabytes the screen can't even show, paid for by the visitor's data plan and your load time.
With srcset you hand the browser the whole menu of sizes and let it order the cup that fits the cup-holder ( sizes ). The phone sips a 400px file; the 4K monitor gets the 2000px one. Everyone gets a full cup, nobody pays for spillage — and because you reserved the right-sized cup-holder up front ( aspect-ratio ), the table never wobbles while the drink arrives.
Images are usually the heaviest thing on a page — often 60–80% of its total weight. Resolution switching means shipping the same picture at several file sizes and letting the browser download whichever fits the screen. You list the files in srcset , each tagged with its real pixel width using the w descriptor ( 800w means "this file is 800 pixels wide").
On its own, srcset isn't enough — the browser chooses the file before it has laid out the page, so it can't yet know how wide the image will appear. That's the job of sizes : it tells the browser, for each viewport width, how wide the image's slot will be. The browser combines sizes with the device's pixel density to pick the smallest file that still looks sharp.
Run the worked example, then resize the preview. Read every comment — they state exactly what each attribute does.
Where srcset serves the same image at different sizes, picture lets you serve different images entirely . This is called art direction : a wide landscape crop on desktop, but a tight square or portrait crop on mobile so the subject stays large enough to see. You wrap one or more source elements and a final fallback img inside it.
The browser reads the source elements top to bottom and uses the first one that matches . Each source can match on a media query (for art direction) or a type (for formats like image/avif and image/webp ). The trailing img is mandatory — it's both the fallback for old browsers and the element that actually carries the alt text.
loading="lazy" tells the browser to defer downloading an image until the user scrolls near it. On a long gallery that can turn dozens of downloads into just the two or three that are actually on screen, slashing your initial page weight. The catch: never lazy-load an image that's visible on first paint (your hero) — deferring it delays your Largest Contentful Paint and hurts Core Web Vitals.
decoding="async" is a smaller win that pairs nicely: it lets the browser decode the image off the main thread so a big picture can't briefly freeze scrolling or interaction. Use it on content images you're not racing to paint immediately.
Ever seen text leap down the page as an image pops in? That's Cumulative Layout Shift (CLS) , a Core Web Vital you can score badly on. It happens because the browser doesn't know how tall an image will be until the file arrives, so it reserves no space and then shoves everything down once it loads.
Two fixes, used together. First, always put width and height attributes on every img — even when CSS makes it fluid with width: 100%; height: auto; . The browser reads them as a ratio and reserves the right box up front. Second, for CSS-driven boxes (or placeholders), set the aspect-ratio property so the container holds its shape before anything loads.
When you force an image into a fixed box, its own shape rarely matches. By default it stretches and looks squashed. object-fit fixes that — it's like background-size , but for a real img . cover fills the box and crops the overflow (no distortion); contain fits the whole image inside and may leave gaps; fill stretches it (the ugly default).
When cover crops, it crops from the centre by default — which can chop off a head. object-position lets you steer which part survives the crop, e.g. object-position: top; to keep faces in frame.
The image below only has a single src , so every device downloads the big file. Give the browser a menu of sizes to choose from. Fill in the blanks marked ___ , then run it and check the expected result in the comments.
Build a picture that swaps crop at 600px, give the fallback img its dimensions so the page doesn't jump, and crop it to a fixed shape without distortion. Fill in each ___ and verify against the comments.
Support is faded now — only an outline is given. Build one image card that uses everything from this lesson. Lean on the worked examples in sections 1–5 if you get stuck.
You can now ship images that are fast, sharp on every screen, and don't shift the layout. The essentials:
Next up: Transitions , where you'll animate state changes smoothly with CSS.
Practice quiz
What does the descriptor in srcset describe?
- The pixel density of the screen
- The weight in kilobytes
- The real pixel width of the image file
- The widget type
Answer: The real pixel width of the image file. The w descriptor states each file's real pixel width, e.g. image-800.jpg 800w means that file is 800px wide.
Why must you pair descriptors in srcset with a attribute?
- To tell the browser how wide the image slot will be so it can pick the right file
- To enable lazy loading
- To set the aspect ratio
- To add alt text
Answer: To tell the browser how wide the image slot will be so it can pick the right file. sizes tells the browser the slot width at each viewport; without it the browser assumes 100vw and often over-downloads.
When should you reach for the <picture> element instead of plain srcset?
- When only the resolution changes
- When the image is decorative
- When you need lazy loading
- When the actual image changes — a different crop (art direction) or format
Answer: When the actual image changes — a different crop (art direction) or format. Use <picture> for art direction (different crops) or format swaps (AVIF/WebP with a JPEG fallback); srcset is for same-image resolution switching.
What is the role of the trailing <img> inside a <picture> element?
- It is optional decoration
- It is the mandatory fallback and carries the alt text
- It sets the page background
- It must be empty
Answer: It is the mandatory fallback and carries the alt text. The <img> is mandatory: it is the fallback when no <source> matches and it carries the alt text.
How does the browser choose among the <source> elements in a <picture>?
- It uses the first <source> that matches, top to bottom
- It uses the last one
- It uses the smallest file
- It picks randomly
Answer: It uses the first <source> that matches, top to bottom. The browser reads sources top to bottom and uses the first one that matches its media query or supported type.
Which descriptor is best for a fixed-size logo on retina screens?
- The w descriptor
- The vw descriptor
- The x descriptor (e.g. 2x)
- The dpi descriptor
Answer: The x descriptor (e.g. 2x). The x descriptor (e.g. logo@2x.png 2x) describes pixel density and suits fixed-size images like icons and logos.
Which attribute defers an off-screen image's download until you scroll near it?
- decoding="async"
- loading="lazy"
- fetchpriority="low"
- defer
Answer: loading="lazy". loading="lazy" delays downloading until the image nears the viewport — ideal for below-the-fold images.
Should you set width and height attributes even when CSS makes the image fluid with width:100%?
- No, they are ignored
- Only on the hero image
- Only for SVGs
- Yes — they give the intrinsic ratio so the browser reserves space and avoids CLS
Answer: Yes — they give the intrinsic ratio so the browser reserves space and avoids CLS. Even with width:100%; height:auto, the attributes provide the intrinsic aspect ratio so the browser reserves the right box and prevents layout shift.
Which object-fit value fills the box and crops the overflow without distorting the image?
- fill
- cover
- contain
- scale-down
Answer: cover. object-fit: cover fills the box and crops overflow with no distortion; contain letterboxes and fill stretches.
When crops an image, which property steers which part stays visible?
- object-align
- background-position
- object-position
- crop-anchor
Answer: object-position. object-position (e.g. object-position: top) controls which part of the image survives the crop.