DEV Community

Cover image for Stop Guessing srcset: Build a Responsive-Image Selection Drill
Karuha
Karuha

Posted on Originally published at aceround.app

Stop Guessing srcset: Build a Responsive-Image Selection Drill

A responsive image bug is often a reasoning bug, not an HTML bug. Before adding another srcset candidate, write down three values: the image's rendered slot in CSS pixels, the device pixel ratio (DPR), and the candidate widths you actually ship. The smallest candidate that can cover the requirement is usually the useful starting point.

This post turns that sentence into a tiny JavaScript drill you can run before a frontend interview. It is deliberately smaller than a browser's real selection algorithm, but it gives you a clean way to explain srcset, sizes, DPR, and the trade-off between sharpness and transferred bytes.

Flow from layout slot and DPR to the selected responsive-image candidate

What problem are srcset and sizes solving?

An image displayed at 480 CSS pixels needs roughly 960 source pixels on a 2x screen to stay sharp. Sending a 2,000-pixel asset to that slot can waste bytes; sending a 480-pixel asset can look soft. Responsive images let the browser choose among candidates before it fetches one.

The roles are easy to mix up:

Attribute or input What it tells the browser
srcset="... 480w, ... 960w" The intrinsic width of each available file
sizes="(max-width: 600px) 100vw, 50vw" The expected layout slot, in CSS pixels
DPR How many physical pixels each CSS pixel maps to
CSS The actual rendered layout; it must agree with the sizes hint

The important detail: a w descriptor is not a display instruction. It says how wide the file is. The sizes attribute supplies the layout hint that makes choosing a candidate possible.

Build a selection drill

Here is a simplified chooser. It intentionally ignores network quality, browser heuristics, zoom, image format support, and cache state. Real browsers consider more than this. The point is to test the explanation you will give in an interview, not to replace the HTML standard.

import assert from "node:assert/strict";

function chooseWidth({ slotCssPx, dpr, candidates }) {
  if (!Number.isFinite(slotCssPx) || slotCssPx <= 0) {
    throw new TypeError("slotCssPx must be a positive number");
  }
  if (!Number.isFinite(dpr) || dpr <= 0) {
    throw new TypeError("dpr must be a positive number");
  }

  const widths = [...new Set(candidates)]
    .filter((width) => Number.isFinite(width) && width > 0)
    .sort((a, b) => a - b);

  if (widths.length === 0) throw new TypeError("provide at least one width");

  const required = Math.ceil(slotCssPx * dpr);
  return widths.find((width) => width >= required) ?? widths.at(-1);
}

assert.equal(
  chooseWidth({ slotCssPx: 480, dpr: 2, candidates: [480, 960, 1440] }),
  960,
);
assert.equal(
  chooseWidth({ slotCssPx: 320, dpr: 3, candidates: [480, 960, 1440] }),
  960,
);
assert.equal(
  chooseWidth({ slotCssPx: 900, dpr: 2, candidates: [480, 960, 1440] }),
  1440,
);

console.log("responsive-image selection assertions passed");
Enter fullscreen mode Exit fullscreen mode

Run it with node responsive-image-drill.mjs. The first case is the common 480 CSS pixel / 2x DPR example: 960 is the first candidate that meets 960 required source pixels. The second shows that a small mobile layout can still need a large source on a dense screen. The last case makes the compromise explicit: when no candidate is large enough, the script picks the largest available file, which may still be upscaled.

Map the drill to real markup

For a card image that is full width on small screens and half the viewport on larger screens, the markup might look like this:

<img
  src="/images/profile-960.jpg"
  srcset="
    /images/profile-480.jpg 480w,
    /images/profile-960.jpg 960w,
    /images/profile-1440.jpg 1440w
  "
  sizes="(max-width: 600px) 100vw, 50vw"
  width="1440"
  height="960"
  alt="Developer reviewing a pull request"
/>
Enter fullscreen mode Exit fullscreen mode

At a 480-pixel mobile viewport, 100vw tells the browser the slot is about 480 CSS pixels. At a wide viewport, 50vw gives it a smaller slot estimate. width and height reserve the aspect ratio, which helps prevent layout shifts while the image loads.

Do not copy those values blindly. If a sidebar, grid gap, or max-width changes the rendered width, update sizes too. A misleading sizes="100vw" on a narrow card can cause a browser to select a much larger resource than the layout needs.

How do you debug a candidate that looks wrong?

Start with evidence rather than an asset-size guess:

  1. Inspect the image's rendered width in DevTools. This is your observed CSS slot.
  2. Check window.devicePixelRatio, then multiply it by that slot for a useful first estimate.
  3. Open the Network panel in a private window or with cache disabled and see which URL was fetched.
  4. Compare that URL's intrinsic width with the selected slot and DPR.
  5. Change one thing: either the candidate set, sizes, or the layout. Reload and observe again.

This sequence is useful in an interview because it separates diagnosis from a proposed fix. Saying “I would compress the image” skips the key question: was the browser given an accurate choice in the first place?

When should you use <picture> instead?

Use img plus width-descriptor srcset when the image content is the same and only the resolution changes. Use <picture> when the image itself should change, such as a wide desktop crop that would hide the subject on a narrow screen. That is art direction, not just resolution switching.

<picture>
  <source media="(max-width: 600px)" srcset="/images/team-close.jpg" />
  <img src="/images/team-wide.jpg" alt="Product team at a whiteboard" />
</picture>
Enter fullscreen mode Exit fullscreen mode

You can also use <source type="image/avif"> with a fallback format, but treat format support as a separate decision from layout width. Mixing all of those ideas into one untested snippet is how responsive-image configuration becomes hard to reason about.

A concise interview answer

A strong answer does not need to recite the full browser algorithm:

“I first estimate the layout slot in CSS pixels, then account for DPR. With width-based srcset, I use sizes to tell the browser that slot so it can select an appropriate source before downloading it. I verify the selected resource in DevTools, and I keep image dimensions on the element to avoid layout shift. If the mobile composition needs a different crop, I switch to picture rather than shipping a smaller version of the same bad crop.”

That answer names the decision inputs, acknowledges verification, and draws the important boundary between sizing and art direction.

Where to practice the explanation

A code snippet proves that you can calculate a candidate; a mock conversation tests whether you can explain the trade-off when the constraints change. For frontend interview preparation, aceround.app — an AI interview assistant is useful as a practice setting for follow-ups such as “what happens at 3x DPR?” or “why did the browser fetch the 1440w resource?”

Use it after you can explain the fundamentals in your own words. A tool can give you more repetitions, but it should not replace opening DevTools and seeing what the browser actually requested.

References

Disclosure: AI assisted with outlining and copy editing. The examples, constraints, and technical review were checked before publication.

Top comments (0)