Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use srcset and sizes when the image content stays the same but its rendered width changes. Use density descriptors (1x, 2x) when the CSS size is fixed. Use <picture> when the crop or format must change under a condition. In every case, keep a useful fallback <img> with accurate alternative text and intrinsic dimensions.

Choose the responsive-image pattern first

Responsive images solve different problems, and the markup should match the problem rather than simply adding more URLs.

Situation Use What varies Key rule
Fluid or column-based slot srcset with w descriptors plus sizes Downloaded resource width Each w must equal the referenced file’s intrinsic pixel width.
Fixed CSS dimensions on standard and high-density screens srcset with 1x, 2x (and optionally more) Pixel density Do not add sizes or mix w and x descriptors.
Different composition, crop, or format <picture> with ordered <source> elements and a final <img> Art direction or supported format Sources are tested in order; the fallback image remains mandatory.

Width-based images with srcset and sizes

For a fluid image, create several versions of the same composition at known intrinsic widths. The srcset tells the browser which candidates exist; sizes describes the width of the rendered slot. It describes the slot, not the width of a source file.

<img
  src="photo-800.jpg"
  srcset="photo-480.jpg 480w, photo-800.jpg 800w, photo-1200.jpg 1200w"
  sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 800px"
  width="800"
  height="600"
  alt="Describe the meaningful content of the photograph"
>

How the browser reads this markup

  • Below 600 CSS pixels, the slot is expected to be the full viewport width (100vw).
  • From 601 through 1,000 pixels, the slot is expected to be half the viewport (50vw).
  • Above the listed conditions, the final value, 800px, is the default slot width.
  • The browser combines that estimate with device pixel density, available candidates, network conditions, and user preferences. It is a hinting system, so no particular URL is guaranteed in every situation.

Make the fallback src a real, usable image for browsers or contexts that do not use the candidate list. A candidate labelled 800w must actually be 800 pixels wide; a misleading descriptor can cause blurry images or needless downloads.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Designing a sensible candidate set

Start with the smallest slot your layout creates and the largest size at which the image is displayed. Add intermediate widths that match your image pipeline rather than generating dozens of nearly identical files. Keep every candidate the same subject and crop when using width switching. If the image sits in a 320-pixel card rather than filling the viewport, write that card width in sizes; claiming 100vw would make the browser overestimate the need.

MDN illustrates why this matters with its tutorial assets: the 800-pixel example is 128KB and the 480-pixel example is 63KB, a 65KB difference for those specific files. That is an illustration, not a guaranteed saving for every format, encoder, or image.

Fixed-size images and density descriptors

Use density descriptors when CSS keeps the image at one size but screens can provide different pixel densities, such as a 160-by-160 avatar.

<img
  src="avatar.png"
  srcset="avatar.png 1x, avatar-2x.png 2x"
  width="160"
  height="160"
  alt="A concise description of the avatar"
>

Here the CSS size is 160 pixels, while the 2x candidate can contain twice as many intrinsic pixels for a sharper result on a high-density display. Do not add sizes to this pattern, and never combine x and w descriptors in one srcset.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use <picture> for art direction and formats

srcset chooses among resources representing the same image content. It does not promise a different crop. When a portrait crop is better on a phone, or when you want to offer WebP while retaining a JPEG fallback, use <picture>.

<picture>
  <source
    media="(max-width: 600px)"
    srcset="portrait-crop.webp"
    type="image/webp"
  >
  <source
    srcset="landscape-crop.jpg"
    type="image/jpeg"
  >
  <img
    src="landscape-crop.jpg"
    width="1200"
    height="800"
    alt="Describe the image's relevant content"
  >
</picture>

Ordering and fallback behavior

  • The browser evaluates each <source> from top to bottom.
  • A source is skipped when its media condition is false or its declared type is unsupported.
  • The final <img> is the fallback and supplies the accessible name, intrinsic dimensions, and a URL that works without a matching source.

You can also combine art direction with multiple width candidates in a source’s srcset, provided that list itself uses one descriptor type consistently. Keep CSS such as object-fit: cover and object-position on the img when intentional fill or focal-point cropping is required.

Accessibility and layout stability

Write useful alternative text

alt should be the concise text replacement for the image’s meaning. Describe what a reader needs to understand, not the filename and not the fact that an image exists. For a decorative image with no textual meaning, use the appropriate decorative treatment rather than inventing a description.

Reserve the image’s space

Include intrinsic width and height on the fallback img. The browser can reserve the correct aspect-ratio space before downloading the resource, reducing layout shifts. You can still make the image fluid:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
img {
  max-width: 100%;
  height: auto;
  display: block;
}

If alternative sources have materially different aspect ratios, give the fallback dimensions that match its own ratio and use deliberate CSS or a layout container for the art-directed result.

Lazy-load only what is appropriate

loading="lazy" is suitable for images below or near the viewport. Do not automatically lazy-load the prominent image needed for the initial view; verify loading priority for the actual page and layout.

How to validate what your page really loads

  1. Measure the slots. At every relevant breakpoint, inspect the actual rendered width. Account for columns, gutters, max-width containers, and padding.
  2. Encode those measurements. Put the slot logic in sizes; do not assume every image is 100vw.
  3. Check file metadata. Confirm every w descriptor equals the file’s intrinsic width and that candidates depict the intended content.
  4. Set the mobile viewport. Include <meta name="viewport" content="width=device-width"> so responsive breakpoints represent the device’s CSS viewport.
  5. Test viewports and densities. Resize the browser and use device emulation for standard and high-density displays. Test the art-directed crop and format fallback.
  6. Inspect Network. Filter for image requests, disable or enable cache deliberately, and record which URL was fetched. Browser choice can also reflect bandwidth and user settings, so a supplied hint does not force one URL.
  7. Verify failure paths. Turn off support for a format where possible, check a broken candidate, and confirm the fallback, dimensions, and alternative text remain correct.

Web.dev describes the division clearly: “To enable browsers to make these choices, srcset allows you to provide a list of potential sources for populating a single <img>, while sizes allows you to provide the browser with information about how that <img> will be rendered.”

Common mistakes and fixes

The phone downloads the desktop-sized file

Usually the sizes value describes the viewport instead of the real column, or the width descriptors are inaccurate. Measure the slot, express its breakpoint widths, and verify intrinsic file widths.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The image is blurry on a high-density screen

You may be using only one low-resolution candidate for a fixed-size element. Supply density candidates such as 1x and 2x, or use width candidates when the slot itself is fluid.

The crop never changes

srcset is not an art-direction switch. Move the breakpoint-specific crop into ordered <source media> elements inside <picture>.

A newer format fails in one browser

Declare its MIME type on a <source> and leave a broadly supported final <img>. Unsupported types are skipped, allowing the fallback to load.

Layout jumps when the image appears

Add accurate width and height attributes, preserve the ratio in CSS, and check that art-directed assets are not being forced into an incompatible box without an intentional crop rule.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The expected candidate does not appear in Network

Clear or bypass cache, confirm the emulated viewport and device scale, and remember that the browser may choose another candidate based on current bandwidth, preferences, or its own selection algorithm. Inspect the rendered slot rather than judging from the URL alone.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

When you need screenshots of responsive states for documentation, regression checks, or debugging, ScreenshotNeo can capture the page directly. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools to Claude, Cursor, and other MCP clients.

See the full parameter list in the ScreenshotNeo documentation. A one-call image request looks like this:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo includes full-page and lazy-image capture, CSS-selector element shots, dark mode, 12 device presets plus custom viewports, retina scale, PDF controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen-TTL caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage APIs, and an OpenAPI specification. Its parameter names also support the names used by other screenshot APIs, easing migration. Pricing is Free for 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is available on every plan, and yearly billing provides two months free. Start with the free ScreenshotNeo account.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Quick decision checklist

  • Same content, fluid slot: width descriptors plus an accurate sizes.
  • Same content, fixed CSS size: density descriptors only.
  • Different crop or conditional format: ordered <picture> sources and a final fallback <img>.
  • Every image: meaningful alt, intrinsic dimensions, and CSS that matches the intended behavior.
  • Before shipping: test real slots, densities, formats, cache states, and fetched URLs in developer tools.

Frequently Asked Questions

Can I use both w and 2x in one srcset?

No. A single candidate list must use either width descriptors or density descriptors; choose the model that matches the layout problem.

Does sizes resize the image file?

No. It tells the browser how wide the rendered slot is expected to be so it can choose among the files listed in srcset.

Should every image use loading="lazy"?

No. Lazy loading is generally for offscreen or near-viewport images; keep the prominent initial image available for the first render unless testing shows otherwise.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.