Skip to content
Intermediate16 min study

Images, media, and responsive assets

Let the browser choose the right image per device with srcset, sizes, and picture, and load media without hurting Core Web Vitals.

Question progress0 / 10 completed
Start the lesson
Images, media, and responsive assets visual explanation

WHAT YOU WILL BE ABLE TO DO

Learning outcomes

  • Explain images, media, and responsive assets in plain language.
  • Connect the behavior to the underlying browser or framework model.
  • Implement the core pattern and reason through edge cases.
  • Answer common follow-ups without relying on memorized phrases.

01Explain it simply

The browser can pick the right image for a device's size and pixel density on its own, if you describe the available candidates with srcset and sizes, or with picture, instead of shipping one oversized image to every screen.

Think of it like this: it's like a restaurant menu with portion sizes printed on it — you don't hand every customer the family-size plate; you describe the small, medium, and large options and let them choose the one that actually fits, before any food is prepared. The browser does the same thing with sizes and srcset, before any bytes are downloaded.

One-line definition: Let the browser choose the right image per device with srcset, sizes, and picture, and load media without hurting Core Web Vitals.

02Mental model

The <img> element accepts srcset (a list of image URLs, each tagged with a width or pixel-density descriptor) and sizes (a media-condition-driven estimate of the rendered width). The browser combines the two, plus the real viewport and screen density, to pick one candidate before downloading anything — the developer supplies options, the browser makes the decision. <picture> adds <source> elements with a media or type condition, letting the author force a different image entirely (art direction) or a different format (AVIF with a JPEG fallback), which srcset/sizes alone cannot express.

03Step by step

  • List the real rendered widths the image can appear at across breakpoints.
  • Provide a srcset candidate with a width descriptor for each of those sizes.
  • Write a sizes value that matches the actual CSS layout, not a guess.
  • Reach for <picture> with <source> only when the image itself must change — art direction or format — not just its resolution.
  • Set width and height (or aspect-ratio) so the browser reserves space before the image loads.
  • Leave the likely Largest Contentful Paint image eager, and lazy-load images further down the page.

04Working example

HTML
<img  src="hero-800.jpg"  srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"  sizes="(min-width: 768px) 50vw, 100vw"  width="800" height="450"  alt="Product hero shot"/>
<picture>  <source srcset="diagram.avif" type="image/avif" />  <source srcset="diagram.webp" type="image/webp" />  <img src="diagram.jpg" alt="Architecture diagram" width="960" height="540" /></picture>

The first image gives the browser three resolution candidates and a sizes hint tied to the real layout, so a phone downloads the 400w file, not the 1600w one sized for a two-column desktop layout. The picture block tries AVIF, falls back to WebP, then a universally supported JPEG — the browser commits to the first source it understands, and the plain img at the end is what actually renders, and what alt describes, in every case.

05Where it is used

  • Product and marketing hero images across breakpoints
  • Art-directed images that crop differently on mobile vs. desktop
  • Modern formats such as AVIF or WebP with safe fallbacks
  • Preventing layout shift while images load

06Common mistakes

  • Writing sizes as a guess instead of matching the real CSS width at each breakpoint, which silently defeats srcset
  • Reaching for <picture> for plain resolution switching when srcset/sizes already solves that with less markup
  • Omitting width/height and causing layout shift as each image loads in
  • Lazy-loading the largest above-the-fold image, delaying LCP instead of improving it

07Interview answer

How to say it out loud: "srcset gives the browser a list of image candidates at different widths, sizes tells it how wide the image will actually render at the current viewport, and the browser combines those with the screen's pixel density to pick one file before downloading anything — so my job is describing accurate options, not choosing the image myself. picture is a different tool: it's for when the image itself needs to change, either a different crop for art direction or a different format like AVIF with a JPEG fallback, not just a different resolution of the same file."

Explain that srcset/sizes lets the browser choose, not the author — sizes has to describe the real rendered width or the browser's choice is wrong. Keep resolution switching (srcset) clearly separate from art direction or format switching (picture).

A sizes attribute says 100vw but the image actually renders at 50% of the viewport on desktop. What happens?

The browser downloads a larger image than necessary, because it trusts the sizes hint to estimate rendered width before layout is final and has no other way to know the real size; the fix is correcting sizes to match the actual CSS, not adding more srcset candidates.

DDConcept deep dives

Deep dive 1

The browser picks from srcset, it doesn't just pick the largest

srcset lists candidate files with a width or pixel-density descriptor; sizes gives the browser a media-condition-driven estimate of how wide the image will render. Before it downloads anything, the browser combines that estimate with the viewport width and the screen's device pixel ratio to select exactly one candidate. Nothing here is decided by file size or by which candidate is listed first — the developer supplies accurate options and an accurate rendered-width estimate, and the browser makes the actual choice per user.

  • A wrong sizes value causes a wrong choice even with perfect srcset candidates.
  • Density descriptors (1x/2x) and width descriptors (400w) solve different problems and cannot mix in one srcset.
  • The selection happens before layout is finalized, so sizes is necessarily an estimate, not a measurement.

Deep dive 2

picture solves a different problem than srcset

srcset and sizes only ever choose between different resolutions of the same image. picture exists for when the image itself must change: a tighter crop for a narrow viewport that srcset can't express, or an entirely different file format like AVIF or WebP with a JPEG fallback for browsers that don't support it. The browser walks the source elements in order and commits to the first one whose media or type condition it accepts, falling back to the required img if none match.

  • Reach for srcset/sizes first; add picture only when resolution switching genuinely isn't enough.
  • The fallback img inside picture is not optional — it supplies alt text and the guaranteed-supported format.
  • Format fallbacks such as AVIF to WebP to JPEG are ordered from smallest/newest to most compatible.
HTML
<picture>  <source srcset="banner.avif" type="image/avif" />  <source srcset="banner.webp" type="image/webp" />  <img src="banner.jpg" alt="Seasonal banner" width="1200" height="400" /></picture>

A browser that understands AVIF uses the smallest file; one that only understands WebP skips to the second source; anything else renders the plain JPEG img, which is also what alt and dimensions are read from.

Deep dive 3

Image loading strategy is a Core Web Vitals decision, not just a bandwidth one

Two independent choices affect measured performance: reserving layout space with width/height or aspect-ratio prevents surrounding content from jumping once an image loads, which is scored as layout shift; and choosing eager versus lazy loading, plus fetchpriority, determines how early the request for a given image starts, which directly affects whether the Largest Contentful Paint element appears sooner or later. Treating every image the same, all lazy or none, usually optimizes one metric at the expense of the other.

  • Reserve space for every image regardless of loading strategy; that's what prevents layout shift.
  • Lazy-load images below the fold; leave the likely-LCP image eager, and prioritize it if it competes with other early requests.
  • Fixing the wrong image's priority, such as a decorative icon, doesn't move the metric actually being measured against the largest content element.

QAInterview questions and model answers

Attempt each answer aloud before opening it. The model answer shows the depth and precision expected in an interview; it is not a script to memorize.

Intermediate · Conceptual · 1 min · Question 1What decides which image the browser actually downloads for an img with srcset and sizes?Open model answer

Model answer

The browser combines the srcset candidates, each tagged with a width or pixel-density descriptor, with the sizes value, an estimate of rendered width per media condition, the current viewport width, and the screen's device pixel ratio. It picks a candidate before layout is finalized and before any bytes are downloaded, using sizes as its best guess of rendered width.

Open question page →
Intermediate · Conceptual · 1 min · Question 2Why doesn't adding more entries to srcset fix a badly guessed sizes attribute?Open model answer

Model answer

sizes tells the browser how wide the image will render at the current viewport; if that guess is wrong, the browser's width estimate is wrong regardless of how many resolution candidates exist in srcset, so it can still pick an oversized or undersized file. sizes has to match the real CSS layout, not the other way around.

Open question page →
Intermediate · Conceptual · 1 min · Question 3When should you use picture instead of a single img with srcset?Open model answer

Model answer

Use picture when the image itself needs to change — a different crop for art direction on narrow viewports, or a different file format like AVIF or WebP with a JPEG fallback — since source conditions can select by media query or by MIME type. srcset and sizes alone only ever resolve to different resolutions of the same image.

Open question page →
Beginner · Conceptual · 1 min · Question 4What determines which source in a picture element the browser uses?Open model answer

Model answer

The browser evaluates source elements in document order and uses the first one whose media condition matches and whose type, if present, it supports; if none match, it falls back to the img element, which is required and also supplies the alt text used in every case.

Open question page →
Intermediate · Conceptual · 1 min · Question 5Why do width and height attributes matter even though CSS controls the final rendered size?Open model answer

Model answer

They let the browser compute the image's aspect ratio before the file has downloaded, so it can reserve the correct box in layout immediately; omitting them causes surrounding content to jump once the image loads, which is measured as layout shift.

Open question page →
Intermediate · Conceptual · 1 min · Question 6What's the risk of adding loading="lazy" to every image on a page, including the hero image?Open model answer

Model answer

loading="lazy" defers offscreen images, which helps images far down the page, but applying it to the largest above-the-fold image delays exactly the request you want to start earliest, often making it load later even though it's the page's Largest Contentful Paint element.

Open question page →
Advanced · Conceptual · 1 min · Question 7What does fetchpriority="high" do, and when is it appropriate?Open model answer

Model answer

It's a hint that raises an image's priority relative to other resources competing for bandwidth early in the page load, useful on the one image most likely to be the LCP element; overusing it on many images removes the signal's value since not everything can be highest priority.

Open question page →
Advanced · Conceptual · 1 min · Question 8How does a pixel-density descriptor like 2x differ from a width descriptor like 400w in srcset?Open model answer

Model answer

A density descriptor tells the browser to pick a candidate based purely on device pixel ratio, independent of layout width, which suits fixed-size images like icons or avatars. A width descriptor requires a sizes attribute so the browser can combine rendered width with pixel density, and the two descriptor types cannot be mixed in one srcset.

Open question page →
Advanced · Conceptual · 1 min · Question 9Can a CSS background-image be lazily loaded the same way as an img?Open model answer

Model answer

Not with the loading attribute, since that only applies to img and iframe. A CSS background image is fetched as soon as the browser determines the rule applies to a rendered element, so deferring it needs a different technique, such as swapping the background via JavaScript once the element nears the viewport.

Open question page →
Advanced · Conceptual · 1 min · Question 10Why is decoding="async" sometimes added to an img?Open model answer

Model answer

It tells the browser it doesn't need to block rendering of other content while decoding that particular image, which can reduce jank when a large image finishes downloading but hasn't been decoded yet; it doesn't change what or when the image is fetched, only how its decode is scheduled relative to other work.

Open question page →

SCScenario questions

Scenario 1

A marketing page's hero image looks correct on desktop, but mobile users on a slow connection see a multi-second delay before anything paints, and Lighthouse flags a large LCP image.

  1. Check the actual srcset candidates and confirm a small mobile-sized file exists.
  2. Verify sizes reflects the real rendered width on mobile, not a leftover desktop guess.
  3. Confirm the hero image isn't lazy-loaded, and consider fetchpriority="high".
  4. Re-measure LCP after the fix to confirm the smaller candidate is now selected.
Reveal worked answer

I'd start by checking what the browser is actually choosing, not assume the code is broken — the network panel shows which srcset candidate was requested. If it's downloading the 1600w desktop file on a 400px-wide phone, the likely cause is a sizes value that doesn't match the real CSS layout at that breakpoint. I'd also confirm the image isn't accidentally lazy-loaded despite being above the fold, add fetchpriority="high" if it's the LCP candidate, then re-run the trace to confirm the smaller file is selected and LCP improves.

Verify and go deeper