Guías HTML para srcset
Aprende a identificar y corregir errores comunes de validación HTML marcados por el W3C Validator, para que tus páginas cumplan con los estándares y se muestren correctamente en todos los navegadores. También consulta nuestras Guías de accesibilidad.
The <picture> element exists to give browsers a choice between multiple image sources. The browser evaluates each <source> in order, looking for the first one whose conditions match the current environment. These conditions are expressed through the media attribute (e.g., viewport width or resolution) and the type attribute (e.g., image/webp or image/avif). If a <source> lacks both attributes, it acts as an unconditional match — the browser will always select it, making any subsequent <source> elements or an <img> with srcset unreachable. This defeats the purpose of the <picture> element entirely.
The HTML specification requires these attributes specifically to prevent this situation. When a <source> has a following sibling <source> or a following <img> with srcset, at least one selection criterion (media or type) must be present so the browser can meaningfully choose between the options. A <source> without these attributes is only valid if it’s the last <source> before a plain <img> (one without srcset), since in that case it serves as the final fallback within the <picture>.
This matters for several reasons:
- Standards compliance: The HTML living standard explicitly defines this requirement. Violating it produces a validation error.
- Predictable rendering: Without distinguishing attributes, browsers may silently ignore sources or always pick the first one, leading to inconsistent behavior across browsers.
- Performance: The <picture> element is often used to serve smaller images on small viewports or modern formats like WebP and AVIF to browsers that support them. Without proper media or type attributes, these optimizations won’t work as intended.
How to fix it
Add a media attribute, a type attribute, or both to each <source> element that is followed by another <source> or an <img> with srcset:
- Use type when you’re offering the same image in different formats (e.g., AVIF, WebP, JPEG). The browser picks the first format it supports.
- Use media when you’re serving different images based on viewport conditions (art direction). The browser picks the source whose media query matches.
- Use both when you want to combine format negotiation with art direction.
Examples
Incorrect — <source> without media or type
Each <source> below has no selection criterion, so the browser has no way to choose between them:
<picture>
<source srcset="hero.webp">
<source srcset="hero.jpg">
<img src="hero.jpg" srcset="hero-2x.jpg 2x" alt="A mountain landscape">
</picture>
Correct — using type for format negotiation
Adding type lets the browser pick the first format it supports:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" srcset="hero-2x.jpg 2x" alt="A mountain landscape">
</picture>
Correct — using media for art direction
Adding media lets the browser pick the source that matches the viewport:
<picture>
<source srcset="hero-wide.jpg" media="(min-width: 1024px)">
<source srcset="hero-narrow.jpg" media="(max-width: 1023px)">
<img src="hero-narrow.jpg" srcset="hero-narrow-2x.jpg 2x" alt="A mountain landscape">
</picture>
Correct — combining media and type
You can use both attributes together to serve the right format at the right viewport size:
<picture>
<source srcset="hero-wide.avif" media="(min-width: 1024px)" type="image/avif">
<source srcset="hero-wide.webp" media="(min-width: 1024px)" type="image/webp">
<source srcset="hero-narrow.avif" media="(max-width: 1023px)" type="image/avif">
<source srcset="hero-narrow.webp" media="(max-width: 1023px)" type="image/webp">
<img src="hero-narrow.jpg" alt="A mountain landscape">
</picture>
Correct — single <source> before a plain <img>
When there’s only one <source> and the <img> has no srcset, no media or type is required — but adding type is still recommended for clarity:
<picture>
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="A mountain landscape">
</picture>
The srcset attribute on img and source elements accepts a comma-separated list of image candidate strings. Each candidate consists of a URL optionally followed by a width descriptor (e.g., 300w) or a pixel density descriptor (e.g., 2x). The URL in each candidate must conform to the URL Standard, which does not permit raw square brackets in the query string of an HTTP or HTTPS URL.
This issue commonly arises when a backend framework or CMS generates URLs that use square brackets in query parameters — for example, ?filter[size]=large or ?dimensions[]=300. While many browsers are lenient and will load these URLs anyway, they are technically invalid according to the URL specification. Using invalid URLs can lead to unpredictable behavior across different browsers, HTML parsers, and tools that process your markup. It also means your HTML fails W3C validation, which can mask other, more critical issues in your code.
You have two main approaches to fix this:
-
Percent-encode the brackets. Replace every [ with %5B and every ] with %5D. This preserves the intended query parameter structure while making the URL spec-compliant. Your server should interpret percent-encoded brackets identically to raw brackets.
-
Eliminate brackets from the URL. If you control the server-side code, consider using alternative query parameter conventions that don’t rely on brackets — for instance, using dot notation (filter.size=large), comma-separated values (dimensions=300,400), or repeated parameter names (dimension=300&dimension=400).
When fixing these URLs, also make sure each image candidate follows the correct format: a valid URL, followed by optional whitespace and a descriptor, with candidates separated by commas.
Examples
Incorrect — raw square brackets in query string
This triggers the validation error because [ and ] appear unescaped in the srcset URLs:
<img
src="photo.jpg"
srcset="photo.jpg?crop[width]=400 400w, photo.jpg?crop[width]=800 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A landscape photo">
Fixed — percent-encoded brackets
Replacing [ with %5B and ] with %5D makes the URLs valid:
<img
src="photo.jpg"
srcset="photo.jpg?crop%5Bwidth%5D=400 400w, photo.jpg?crop%5Bwidth%5D=800 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A landscape photo">
Fixed — brackets removed from URL design
If you can modify the server-side routing, restructuring the query parameters avoids the issue entirely:
<img
src="photo.jpg"
srcset="photo.jpg?crop_width=400 400w, photo.jpg?crop_width=800 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A landscape photo">
Incorrect — brackets with pixel density descriptors
The same problem occurs regardless of whether you use width descriptors or density descriptors:
<img
src="avatar.jpg"
srcset="avatar.jpg?size=[sm] 1x, avatar.jpg?size=[lg] 2x"
alt="User avatar">
Fixed — percent-encoded version
<img
src="avatar.jpg"
srcset="avatar.jpg?size=%5Bsm%5D 1x, avatar.jpg?size=%5Blg%5D 2x"
alt="User avatar">
Fixed — simplified query parameters
<img
src="avatar.jpg"
srcset="avatar.jpg?size=sm 1x, avatar.jpg?size=lg 2x"
alt="User avatar">
The srcset attribute allows you to provide multiple image sources so the browser can choose the most appropriate one based on the user’s device and viewport. There are two distinct modes for srcset:
- Width descriptor mode — Each candidate specifies the image’s intrinsic width using a w descriptor (e.g., 640w). This mode requires the sizes attribute so the browser knows how much space the image will occupy in the layout, enabling it to pick the best candidate.
- Density descriptor mode — Each candidate specifies a pixel density using an x descriptor (e.g., 2x). This mode does not use the sizes attribute; the browser simply matches candidates to the device’s pixel density.
These two modes are mutually exclusive. You cannot mix w and x descriptors in the same srcset, and you cannot use x descriptors (or bare URLs with no descriptor) when sizes is present. The HTML specification is explicit about this: if sizes is specified, all image candidate strings must include a width descriptor.
Why this matters
- Standards compliance: The WHATWG HTML Living Standard defines strict parsing rules for srcset. When sizes is present, the browser’s source selection algorithm expects width descriptors. Providing density descriptors or bare candidates in this context violates the spec and produces undefined behavior.
- Broken image selection: Browsers rely on the sizes attribute to calculate which w-described image best fits the current layout width. If you provide x descriptors alongside sizes, the browser may ignore the srcset entirely or fall back to the src attribute, defeating the purpose of responsive images.
- Accessibility and performance: Responsive images exist to serve appropriately sized files to different devices. An invalid srcset/sizes combination can result in oversized images being downloaded on small screens (wasting bandwidth) or undersized images on large screens (reducing visual quality).
How to fix it
You have two options:
- Keep sizes and switch to width descriptors — Replace every x descriptor (or missing descriptor) in srcset with the actual intrinsic pixel width of each image file, expressed with a w suffix.
- Remove sizes and keep density descriptors — If you only need to serve different resolutions for high-DPI screens at a fixed layout size, drop the sizes attribute and use x descriptors.
When using width descriptors, the value must match the image file’s actual intrinsic width in pixels. For example, if photo-640.jpg is 640 pixels wide, its descriptor should be 640w.
Examples
Invalid: sizes present with density descriptors
This triggers the error because 1x and 2x are density descriptors, but sizes requires width descriptors.
<img
src="photo-640.jpg"
srcset="photo-640.jpg 1x, photo-1280.jpg 2x"
sizes="(max-width: 600px) 100vw, 600px"
alt="A mountain landscape">
Invalid: sizes present with a bare candidate (no descriptor)
A candidate with no descriptor defaults to 1x, which is a density descriptor — still invalid when sizes is present.
<img
src="photo-640.jpg"
srcset="photo-640.jpg, photo-1280.jpg 2x"
sizes="(max-width: 600px) 100vw, 600px"
alt="A mountain landscape">
Fix: use width descriptors with sizes
Each candidate now specifies the intrinsic width of the image file. The browser uses the sizes value to determine which image to download.
<img
src="photo-640.jpg"
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1280.jpg 1280w"
sizes="(max-width: 600px) 100vw, 600px"
alt="A mountain landscape">
Alternative fix: remove sizes and use density descriptors
If you don’t need the browser to choose images based on layout width — for example, the image always renders at a fixed CSS size — drop sizes and use x descriptors.
<img
src="photo-640.jpg"
srcset="photo-640.jpg 1x, photo-1280.jpg 2x"
alt="A mountain landscape">
Using width descriptors with source inside picture
The same rule applies to source elements inside a picture. If sizes is present, every candidate must use a w descriptor.
<picture>
<source
srcset="hero-480.webp 480w, hero-960.webp 960w, hero-1920.webp 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
type="image/webp">
<img
src="hero-960.jpg"
srcset="hero-480.jpg 480w, hero-960.jpg 960w, hero-1920.jpg 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="A hero banner image">
</picture>
The srcset attribute supports two types of descriptors: width descriptors (like 480w) and pixel density descriptors (like 2x). However, these two types cannot be mixed, and the sizes attribute is only compatible with width descriptors. The sizes attribute tells the browser how wide the image will be displayed at various viewport sizes, and the browser uses this information along with the width descriptors in srcset to choose the most appropriate image file. If sizes is present but an image candidate lacks a width descriptor, the browser cannot perform this calculation correctly.
This matters for several reasons. First, it violates the WHATWG HTML specification, which explicitly requires that when sizes is present, all image candidates must use width descriptors. Second, browsers may ignore malformed srcset values or fall back to unexpected behavior, resulting in the wrong image being loaded — potentially hurting performance by downloading unnecessarily large files or degrading visual quality by selecting a too-small image. Third, standards-compliant markup ensures consistent, predictable behavior across all browsers and devices.
A common mistake is specifying a plain URL without any descriptor, or mixing density descriptors (1x, 2x) with the sizes attribute. An image candidate string without any descriptor defaults to 1x, which is a density descriptor — and that conflicts with the presence of sizes.
Examples
❌ Incorrect: Missing width descriptor with sizes present
<picture>
<source
srcset="image-small.jpg, image-large.jpg 1024w"
sizes="(max-width: 600px) 480px, 800px">
<img src="image-fallback.jpg" alt="A scenic landscape">
</picture>
Here, image-small.jpg has no width descriptor. Since sizes is present, this triggers the validation error.
❌ Incorrect: Using density descriptors with sizes
<img
srcset="image-1x.jpg 1x, image-2x.jpg 2x"
sizes="(max-width: 600px) 480px, 800px"
src="image-fallback.jpg"
alt="A scenic landscape">
Density descriptors (1x, 2x) are incompatible with the sizes attribute.
✅ Correct: All candidates have width descriptors
<picture>
<source
srcset="image-small.jpg 480w, image-large.jpg 1024w"
sizes="(max-width: 600px) 480px, 800px">
<img src="image-fallback.jpg" alt="A scenic landscape">
</picture>
Every image candidate now includes a width descriptor, which pairs correctly with the sizes attribute.
✅ Correct: Using density descriptors without sizes
If you want to use density descriptors instead of width descriptors, simply remove the sizes attribute:
<img
srcset="image-1x.jpg 1x, image-2x.jpg 2x"
src="image-fallback.jpg"
alt="A scenic landscape">
This is valid because density descriptors don’t require (and shouldn’t be used with) the sizes attribute.
✅ Correct: Width descriptors on <img> with sizes
<img
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1280.jpg 1280w"
sizes="(max-width: 400px) 320px, (max-width: 800px) 640px, 1280px"
src="photo-640.jpg"
alt="A close-up of a flower">
Each entry in srcset specifies its intrinsic width, and sizes tells the browser which display width to expect at each breakpoint. The browser then selects the best-fitting image automatically.
The srcset attribute allows you to provide the browser with a set of image sources to choose from based on the user’s viewport size or display density. Each entry in srcset is called an image candidate string and consists of a URL followed by an optional descriptor — either a width descriptor (like 300w) or a pixel density descriptor (like 2x).
The sizes attribute tells the browser what display size the image will occupy at various viewport widths, using media conditions and length values. The browser uses this size information together with the width descriptors in srcset to select the most appropriate image. This is why the HTML specification requires that when sizes is present, all srcset entries must use width descriptors — without them, the browser cannot perform the size-based selection that sizes is designed to enable.
This error typically appears in three situations:
- A srcset entry has no descriptor at all — the URL is listed without any accompanying width or density value.
- A pixel density descriptor (x) is used alongside sizes — mixing sizes with x descriptors is invalid because the two mechanisms are mutually exclusive.
- A typo or formatting issue — for example, writing 600px instead of 600w, or placing a comma incorrectly.
Why this matters
- Standards compliance: The WHATWG HTML Living Standard explicitly states that when sizes is specified, all image candidates must use width descriptors.
- Correct image selection: Without proper width descriptors, browsers cannot accurately determine which image to download. This may lead to unnecessarily large downloads on small screens or blurry images on large screens.
- Performance: Responsive images are a key performance optimization. A malformed srcset defeats the purpose and can result in wasted bandwidth.
How to fix it
- Determine the intrinsic width (in pixels) of each image file listed in srcset.
- Append the width descriptor to each URL in the format [width]w, where [width] is the image’s actual pixel width.
- Ensure no entries use x descriptors when sizes is present. If you need density descriptors, remove the sizes attribute entirely.
- Make sure every entry has a descriptor — bare URLs without any descriptor are invalid when sizes is used.
Examples
Missing width descriptor
This triggers the validation error because the srcset URL has no width descriptor:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg"
sizes="(max-width: 600px) 100vw, 600px"
alt="A sunset over the mountains"
>
Fixed by adding the width descriptor:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg 600w"
sizes="(max-width: 600px) 100vw, 600px"
alt="A sunset over the mountains"
>
Using pixel density descriptors with sizes
This is invalid because x descriptors cannot be combined with the sizes attribute:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg 1x, /img/photo-2x.jpg 2x"
sizes="(max-width: 800px) 100vw, 800px"
alt="A sunset over the mountains"
>
Fixed by switching to width descriptors:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg 800w, /img/photo-2x.jpg 1600w"
sizes="(max-width: 800px) 100vw, 800px"
alt="A sunset over the mountains"
>
Alternatively, if you only need density-based selection and don’t need sizes, remove it:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg 1x, /img/photo-2x.jpg 2x"
alt="A sunset over the mountains"
>
Multiple image sources with width descriptors
A complete responsive image setup with several sizes:
<img
src="/img/photo-800.jpg"
srcset="
/img/photo-400.jpg 400w,
/img/photo-800.jpg 800w,
/img/photo-1200.jpg 1200w
"
sizes="(max-width: 480px) 100vw, (max-width: 960px) 50vw, 800px"
alt="A sunset over the mountains"
>
Each URL is paired with a w descriptor that matches the image’s intrinsic pixel width. The sizes attribute then tells the browser how wide the image will display at each breakpoint, allowing it to pick the best candidate.
The srcset attribute allows you to specify multiple image sources so the browser can choose the most appropriate one based on the user’s device characteristics, such as screen resolution or viewport width. When you include a srcset attribute on an <img> element, the HTML specification requires it to contain one or more comma-separated image candidate strings. Each string consists of a URL followed by an optional descriptor — either a width descriptor (e.g., 200w) or a pixel density descriptor (e.g., 2x).
This validation error typically appears when:
- The srcset attribute is empty (srcset="")
- The srcset attribute contains only whitespace (srcset=" ")
- The value contains syntax errors such as missing URLs, invalid descriptors, or incorrect formatting
- A templating engine or CMS outputs the attribute with no value
This matters because browsers rely on the srcset value to select the best image to load. An empty or malformed srcset means the browser must fall back entirely to the src attribute, making the srcset attribute pointless. Additionally, invalid markup can cause unexpected behavior across different browsers and undermines standards compliance.
How to fix it
- Provide valid image candidate strings. Each entry needs a URL and optionally a width or pixel density descriptor, with entries separated by commas.
- Remove the attribute entirely if you don’t have multiple image sources to offer. A plain src attribute is perfectly fine on its own.
- Check dynamic output. If a CMS or templating system generates the srcset, ensure it conditionally omits the attribute when no responsive image candidates are available, rather than outputting an empty attribute.
Examples
❌ Empty srcset attribute
<img src="/img/photo.jpg" alt="A sunset over the ocean" srcset="">
This triggers the error because srcset is present but contains no image candidate strings.
❌ Malformed srcset value
<img src="/img/photo.jpg" alt="A sunset over the ocean" srcset="1x, 2x">
This is invalid because each candidate string must include a URL. Descriptors alone are not valid entries.
✅ Using pixel density descriptors
<img
src="/img/photo-400.jpg"
alt="A sunset over the ocean"
srcset="
/img/photo-400.jpg 1x,
/img/photo-800.jpg 2x
">
Each candidate string contains a URL followed by a pixel density descriptor (1x, 2x). The browser picks the best match for the user’s display.
✅ Using width descriptors with sizes
<img
src="/img/photo-400.jpg"
alt="A sunset over the ocean"
srcset="
/img/photo-400.jpg 400w,
/img/photo-800.jpg 800w,
/img/photo-1200.jpg 1200w
"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px">
Width descriptors (e.g., 400w) tell the browser the intrinsic width of each image. The sizes attribute then tells the browser how large the image will be displayed at various viewport sizes, allowing it to calculate the best source to download.
✅ Removing srcset when not needed
<img src="/img/photo.jpg" alt="A sunset over the ocean">
If you only have a single image source, simply omit srcset altogether. The src attribute alone is valid and sufficient.
✅ Single candidate in srcset
<img
src="/img/photo.jpg"
alt="A sunset over the ocean"
srcset="/img/photo-highres.jpg 2x">
Even a single image candidate string is valid. Here, the browser will use the high-resolution image on 2x displays and fall back to src otherwise.
The srcset attribute allows you to provide multiple image sources so the browser can choose the most appropriate one based on the user’s viewport size or screen density. There are two distinct modes for srcset:
- Width descriptor mode — each candidate specifies its intrinsic width using a w descriptor (e.g., 400w). This mode requires the sizes attribute so the browser knows how much space the image will occupy in the layout and can calculate which source to download.
- Pixel density descriptor mode — each candidate specifies a pixel density using an x descriptor (e.g., 2x). This mode must not include a sizes attribute.
When you include a sizes attribute but forget to add width descriptors to one or more srcset entries, the browser has incomplete information. The HTML specification explicitly states that if sizes is present, all image candidate strings must use width descriptors. An entry without a descriptor defaults to 1x (a pixel density descriptor), which conflicts with the width descriptor mode triggered by sizes. This mismatch causes the W3C validator to report the error.
Beyond validation, this matters for real-world performance. Responsive images are one of the most effective tools for reducing page weight on smaller screens. If the descriptors are missing or mismatched, browsers may download an image that is too large or too small, hurting both performance and visual quality.
How to fix it
You have two options depending on your use case:
Option 1: Add width descriptors to all srcset candidates
If you need the browser to select images based on viewport size (the most common responsive images pattern), keep the sizes attribute and ensure every srcset entry has a w descriptor that matches the image’s intrinsic pixel width.
Option 2: Remove sizes and use pixel density descriptors
If you only need to serve higher-resolution images for high-DPI screens (e.g., Retina displays) and the image always renders at the same CSS size, remove the sizes attribute and use x descriptors instead.
Examples
❌ Incorrect: sizes present but srcset entry has no width descriptor
<img
src="photo-800.jpg"
srcset="photo-400.jpg, photo-800.jpg"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
Both srcset entries lack a width descriptor. Because sizes is present, the validator reports an error for each candidate.
✅ Correct: sizes present with width descriptors on every candidate
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
Each candidate now specifies its intrinsic width (400w and 800w), which tells the browser the actual pixel width of each source file. The browser combines this with the sizes value to pick the best match.
❌ Incorrect: mixing width descriptors and bare entries
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
The second candidate (photo-800.jpg) is missing its width descriptor. All candidates must have one when sizes is present — not just some of them.
✅ Correct: pixel density descriptors without sizes
<img
src="photo-800.jpg"
srcset="photo-800.jpg 1x, photo-1600.jpg 2x"
alt="A mountain landscape">
Here the sizes attribute is removed, and each srcset entry uses a pixel density descriptor (1x, 2x). This is valid and appropriate when the image always occupies the same CSS dimensions regardless of viewport width.
❌ Incorrect: using sizes with pixel density descriptors
<img
src="photo-800.jpg"
srcset="photo-800.jpg 1x, photo-1600.jpg 2x"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
The sizes attribute and x descriptors cannot be combined. Either switch to w descriptors or remove sizes.
Quick reference
| Pattern | srcset descriptor | sizes required? |
|---|---|---|
| Viewport-based selection | Width (w) | Yes |
| Density-based selection | Pixel density (x) | No — must be omitted |
Remember that the w value in srcset refers to the image file’s intrinsic pixel width (e.g., an 800-pixel-wide image gets 800w), while values in sizes use CSS length units like px, vw, or em to describe how wide the image will render in the layout.
The srcset attribute lets browsers intelligently choose which image to load based on the viewport size and device pixel ratio. Each entry in a srcset consists of a URL followed by either a width descriptor (like 300w) or a pixel density descriptor (like 2x). When using width descriptors, the value represents the intrinsic pixel width of the image file — that is, the actual width of the image as stored on disk.
A width descriptor of 0w violates the HTML specification, which requires width descriptors to be integers greater than zero. A zero-width image cannot meaningfully participate in the browser’s source selection process. The browser uses these width values in combination with the sizes attribute to calculate which image best fits the current layout — a value of zero would break this calculation entirely.
This issue commonly occurs when:
- Image dimensions are dynamically generated and a fallback of 0 is used for missing data.
- A placeholder or empty state is accidentally included in the srcset.
- A CMS or build tool outputs a 0w descriptor for images whose dimensions weren’t computed.
Why it matters
- Standards compliance: The HTML specification explicitly requires width descriptors to be positive integers. Validators will flag 0w as an error.
- Browser behavior: While browsers may silently ignore the invalid entry, you can’t rely on consistent handling across all browsers and versions. The image selection algorithm may behave unpredictably.
- Performance: A well-formed srcset is key to responsive image loading. Invalid descriptors can prevent browsers from selecting the optimal image, leading to unnecessarily large downloads or poor image quality.
How to fix it
- Open the image file associated with the 0w descriptor and check its actual pixel width using an image editor or the command line.
- Replace 0w with the correct width (e.g., 150w for a 150-pixel-wide image).
- If the image is truly zero-width or a placeholder, remove that entry from the srcset entirely.
- Ensure every remaining entry has a unique, positive width descriptor.
Examples
❌ Invalid: width descriptor of 0w
<picture>
<source
srcset="/images/icon_placeholder.png 0w,
/images/icon_large.png 600w"
media="(max-width: 600px)">
<img src="/images/icon_fallback.png" alt="App logo">
</picture>
The 0w descriptor triggers the validation error because zero is not a valid width.
✅ Fixed: all width descriptors are positive
<picture>
<source
srcset="/images/icon_small.png 300w,
/images/icon_large.png 600w"
media="(max-width: 600px)">
<img src="/images/icon_fallback.png" alt="App logo">
</picture>
Each entry now has a meaningful width descriptor (300w and 600w) that reflects the actual pixel width of the corresponding image.
❌ Invalid: 0w on an <img> element
<img
srcset="/images/hero_tiny.jpg 0w,
/images/hero_medium.jpg 800w,
/images/hero_large.jpg 1200w"
sizes="100vw"
src="/images/hero_medium.jpg"
alt="Mountain landscape">
✅ Fixed: placeholder entry removed or corrected
If the tiny image is 400 pixels wide, use 400w:
<img
srcset="/images/hero_tiny.jpg 400w,
/images/hero_medium.jpg 800w,
/images/hero_large.jpg 1200w"
sizes="100vw"
src="/images/hero_medium.jpg"
alt="Mountain landscape">
Alternatively, if the image doesn’t belong in the set at all, simply remove it:
<img
srcset="/images/hero_medium.jpg 800w,
/images/hero_large.jpg 1200w"
sizes="100vw"
src="/images/hero_medium.jpg"
alt="Mountain landscape">
When using a build tool or CMS that generates srcset values dynamically, add a check to filter out any entries where the computed width is zero or missing before rendering the attribute. This prevents the invalid markup from reaching production.
The <source> element is used inside <picture>, <audio>, or <video> elements to specify alternative media resources. When used inside a <picture> element, the srcset attribute is required and must contain one or more comma-separated image candidate strings. Each image candidate string consists of a URL and an optional descriptor — either a width descriptor like 400w or a pixel density descriptor like 2x.
This validation error typically occurs when:
- The srcset attribute is present but empty (srcset="").
- The attribute value contains only whitespace.
- The value is malformed or contains syntax errors (e.g., missing URLs, invalid descriptors).
- A dynamic templating system or CMS outputs the attribute with no value.
Why this matters
Browsers rely on the srcset attribute to select the most appropriate image to display based on the user’s device capabilities, viewport size, and network conditions. An empty or invalid srcset means the browser cannot perform this selection, potentially resulting in no image being displayed at all. This degrades the user experience, harms accessibility (screen readers and assistive technologies may encounter unexpected behavior), and violates the HTML specification as defined by the WHATWG living standard.
How to fix it
- Provide at least one valid image URL in the srcset attribute.
- Optionally add descriptors — use width descriptors (w) when combined with the sizes attribute, or pixel density descriptors (x) for fixed-size images.
- If you have no image to provide, remove the <source> element entirely rather than leaving srcset empty.
- Check dynamic output — if a CMS or templating engine generates the srcset value, add a conditional check to omit the <source> element when no images are available.
Examples
❌ Empty srcset attribute
<picture>
<source srcset="" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
This triggers the error because srcset is present but contains no image candidate strings.
❌ Invalid descriptor syntax
<picture>
<source srcset="photo.webp 400" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
This is invalid because 400 is not a recognized descriptor — it must be 400w or a density descriptor like 2x.
✅ Single image candidate
<picture>
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
A single URL without a descriptor is valid and serves as the default 1x candidate.
✅ Multiple candidates with width descriptors
<picture>
<source
srcset="photo-small.webp 400w, photo-medium.webp 800w, photo-large.webp 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
This provides three image candidates with width descriptors, allowing the browser to choose the best match based on the viewport and display density.
✅ Multiple candidates with pixel density descriptors
<picture>
<source srcset="photo.webp 1x, photo-2x.webp 2x" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
Pixel density descriptors tell the browser which image to use based on the device’s pixel ratio — 1x for standard displays and 2x for high-DPI (Retina) screens.
✅ Removing the source element when no image is available
If your application dynamically generates the srcset value and sometimes has no image to provide, omit the <source> element entirely:
<picture>
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
This is valid because the <img> element inside <picture> serves as the required fallback and can stand alone.
The srcset attribute supports two types of descriptors: width descriptors (e.g., 480w) and pixel density descriptors (e.g., 2x). These two types cannot be mixed, and the sizes attribute is specifically designed to work with width descriptors. The sizes attribute tells the browser how wide the image will be displayed at various viewport sizes, so the browser can then pick the best image from srcset based on the widths you’ve provided. If any candidate in srcset lacks a width descriptor — or uses a density descriptor instead — the browser can’t perform this calculation, and the HTML is invalid.
This matters for several reasons. First, browsers rely on the combination of sizes and width descriptors to make intelligent decisions about which image to download before the layout is computed. An invalid srcset can lead to the browser ignoring the entire attribute or selecting a suboptimal image, wasting bandwidth or displaying a blurry result. Second, standards compliance ensures consistent behavior across all browsers and devices.
A common mistake is specifying sizes while using density descriptors (1x, 2x) or providing bare URLs without any descriptor in srcset. If you want to use density descriptors, simply remove the sizes attribute. If you want responsive image selection based on viewport width, use width descriptors for every candidate.
Examples
Incorrect: Using density descriptors with sizes
<picture>
<source
srcset="image-small.jpg 1x, image-large.jpg 2x"
sizes="(max-width: 600px) 100vw, 50vw">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
This triggers the error because 1x and 2x are density descriptors, but the sizes attribute requires width descriptors.
Incorrect: Missing descriptor on one candidate
<picture>
<source
srcset="image-small.jpg, image-large.jpg 800w"
sizes="(max-width: 600px) 100vw, 50vw">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
Here, image-small.jpg has no descriptor at all. When sizes is present, every candidate must have a width descriptor.
Correct: All candidates use width descriptors with sizes
<picture>
<source
srcset="image-small.jpg 400w, image-large.jpg 800w"
sizes="(max-width: 600px) 100vw, 50vw">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
Each image candidate now specifies a width descriptor (400w, 800w), which matches the requirement imposed by the sizes attribute.
Correct: Using density descriptors without sizes
If you only need density-based selection (e.g., for retina displays) and don’t need viewport-based sizing, remove the sizes attribute entirely:
<picture>
<source srcset="image-small.jpg 1x, image-large.jpg 2x">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
Correct: Using srcset with width descriptors on <img>
The same rules apply when using srcset directly on an <img> element:
<img
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1024.jpg 1024w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 33vw"
src="photo-640.jpg"
alt="A mountain landscape">
Every candidate in srcset includes a width descriptor, making this fully valid alongside the sizes attribute. The src attribute serves as the fallback for browsers that don’t support srcset.
Every <img> element must include at least a src or a srcset attribute to be valid HTML.
The <img> element exists to embed an image into the document, and it needs to know where that image is. The src attribute provides a single URL for the image, while srcset lets you offer multiple image sources for different screen sizes or resolutions.
You might run into this error when using JavaScript to set the image source dynamically, or when using lazy-loading libraries that store the URL in a data- attribute like data-src. While those techniques work at runtime, they produce invalid HTML because the validator still expects src or srcset to be present in the markup.
If you genuinely don’t have a source yet, you can use a placeholder or a transparent pixel as the src value.
Invalid Example
<img alt="A cute cat" loading="lazy" data-src="cat.jpg">
Valid Examples
Using src:
<img src="cat.jpg" alt="A cute cat">
Using srcset:
<img srcset="cat-400.jpg 400w, cat-800.jpg 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A cute cat">
Using both src and a lazy-loading data-src (keeps the markup valid while still supporting lazy loading):
<img src="placeholder.jpg" data-src="cat.jpg" alt="A cute cat">
The sizes attribute and the srcset attribute work together as a system for responsive images. The srcset attribute provides the browser with a list of image candidates (typically at different widths or pixel densities), while the sizes attribute tells the browser how much space the image will occupy in the layout. The browser uses both pieces of information together to pick the most appropriate image file to download.
When you specify sizes without srcset, the attribute has no purpose. There’s only one image source (the src attribute), so the browser has nothing to choose from, and the layout hints provided by sizes are meaningless. The HTML specification explicitly states that the sizes attribute must not be present unless srcset is also specified with width descriptors (w). This isn’t just a stylistic concern — it signals to validators and other tools that the markup is incomplete or incorrect, which could indicate a copy-paste error or a missing attribute.
This issue commonly occurs when:
- The srcset attribute is accidentally removed during refactoring, leaving sizes orphaned.
- A developer adds sizes in preparation for responsive images but forgets to add srcset.
- Code is copied from a template and partially modified.
Examples
❌ Invalid: sizes without srcset
<img
src="photo.jpg"
sizes="(max-width: 600px) 100vw, 50vw"
alt="A landscape photo">
The sizes attribute is present, but there is no srcset to provide multiple image candidates. The browser has no use for the sizing information.
✅ Fix: Add a matching srcset attribute
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="A landscape photo">
Now sizes tells the browser: “Below 600px viewports, the image fills 100% of the viewport width; otherwise it fills 50%.” The browser combines this with the width descriptors in srcset to select the best image.
✅ Fix: Remove sizes if you don’t need responsive images
<img
src="photo.jpg"
alt="A landscape photo">
If you only have a single image source and don’t need responsive behavior, simply remove the sizes attribute.
✅ Using sizes with <source> inside <picture>
The same rule applies to <source> elements inside a <picture> block:
<picture>
<source
srcset="photo-dark-400.jpg 400w, photo-dark-800.jpg 800w"
sizes="(max-width: 600px) 100vw, 50vw"
media="(prefers-color-scheme: dark)">
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="A landscape photo">
</picture>
Each element that uses sizes also includes a corresponding srcset with width descriptors.
A note on srcset with pixel density descriptors
The sizes attribute is specifically designed for use with width descriptors (w) in srcset. If you’re using pixel density descriptors (x) instead, sizes is not needed:
<img
src="photo.jpg"
srcset="photo-2x.jpg 2x, photo-3x.jpg 3x"
alt="A landscape photo">
In this case, the browser selects based on device pixel ratio rather than viewport size, so sizes would be unnecessary.
The sizes attribute and the srcset attribute are designed to work as a pair for responsive image delivery. The srcset attribute provides the browser with a list of image files and their intrinsic widths (e.g., 480w, 800w), while the sizes attribute tells the browser how much space the image will occupy in the layout at different viewport sizes. The browser combines this information to select the most appropriate image file to download.
When sizes appears without srcset, it serves no purpose. The browser has only the single image specified in the src attribute, so there’s no decision to make about which image to load. The HTML specification explicitly requires that sizes must not be present unless srcset is also specified with width descriptors.
This error commonly occurs when a CMS or templating system outputs the sizes attribute by default, when srcset is accidentally removed during refactoring, or when developers copy markup snippets without including all the necessary attributes.
Beyond standards compliance, leaving orphaned sizes attributes creates confusing, harder-to-maintain code. Other developers (or your future self) may assume responsive images are configured when they aren’t, leading to wasted debugging time.
How to fix it
You have two options:
- Add a srcset attribute if you want the browser to choose from multiple image sizes based on viewport width. The srcset must use width descriptors (w) for sizes to be meaningful.
- Remove the sizes attribute if you don’t need responsive images and a single src is sufficient.
Note that sizes is also valid on <source> elements inside a <picture> element — the same rule applies there. Every <source> with a sizes attribute must also have a srcset attribute.
Examples
❌ Incorrect: sizes without srcset
<img
src="image.jpg"
sizes="(max-width: 600px) 480px, 800px"
alt="A mountain landscape">
The sizes attribute is present but there’s no srcset, so the browser has no alternative images to pick from.
✅ Correct: sizes paired with srcset
<img
src="image-800w.jpg"
srcset="image-480w.jpg 480w, image-800w.jpg 800w"
sizes="(max-width: 600px) 480px, 800px"
alt="A mountain landscape">
Here, srcset provides two images with their intrinsic widths. The sizes attribute tells the browser: “If the viewport is 600px or narrower, the image will display at 480px wide; otherwise, it will display at 800px wide.” The browser uses this information to download the most efficient file.
✅ Correct: removing sizes when responsive images aren’t needed
<img src="image.jpg" alt="A mountain landscape">
If a single image is sufficient, simply drop the sizes attribute.
❌ Incorrect: sizes on a <source> without srcset
<picture>
<source
media="(min-width: 800px)"
sizes="50vw">
<img src="fallback.jpg" alt="A sunset over the ocean">
</picture>
✅ Correct: sizes on a <source> with srcset
<picture>
<source
media="(min-width: 800px)"
srcset="wide-480w.jpg 480w, wide-960w.jpg 960w"
sizes="50vw">
<img src="fallback.jpg" alt="A sunset over the ocean">
</picture>
The <source> element now includes a srcset with width descriptors, giving the browser the candidate images it needs to make use of sizes.
The srcset attribute lets you provide multiple image sources so the browser can select the most appropriate one. There are two types of descriptors you can use in srcset: pixel density descriptors (e.g., 1x, 2x) and width descriptors (e.g., 400w, 800w). When you use pixel density descriptors, the browser already knows the relationship between each source — it simply picks the one matching the device’s pixel ratio. But width descriptors work differently. They tell the browser the intrinsic pixel width of each image file, and the browser then needs to know how wide the image will actually be rendered on screen to calculate which file is the best fit. That’s exactly what the sizes attribute provides.
The sizes attribute accepts a comma-separated list of media conditions paired with length values, plus a default length. For example, sizes="(max-width: 600px) 100vw, 50vw" tells the browser: “If the viewport is 600px wide or less, this image will occupy 100% of the viewport width; otherwise, it will occupy 50%.” Armed with this information and the width descriptors in srcset, the browser can do the math and download only the most suitable image — before CSS or layout has even been calculated.
Why this matters
- Standards compliance: The HTML specification requires sizes whenever srcset uses width descriptors. Omitting it produces invalid HTML.
- Correct image selection: Without sizes, browsers fall back to assuming the image will be 100vw wide, which often leads to downloading unnecessarily large images on desktop layouts where the image is much smaller than the full viewport.
- Performance: Serving oversized images wastes bandwidth and slows page load. A proper sizes attribute ensures the browser downloads the smallest sufficient image.
- Predictable behavior: Relying on the browser’s fallback assumption (100vw) makes your responsive images behave inconsistently and defeats the purpose of providing multiple candidates.
How to fix it
- Identify every img (or source) element that uses width descriptors in srcset.
- Determine how wide the image will be displayed at different viewport sizes. You can inspect this with your browser’s developer tools or by reviewing your CSS.
- Add a sizes attribute that describes those widths using media conditions and CSS length values like px, vw, em, or calc() expressions.
Examples
Incorrect — missing sizes with width descriptors
<img
src="photo-400.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
alt="A mountain landscape">
This triggers the validation error because the browser sees width descriptors (400w, 800w, 1200w) but has no sizes attribute to determine the image’s rendered width.
Correct — sizes attribute added
<img
src="photo-400.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 600px"
alt="A mountain landscape">
Here, the sizes attribute tells the browser:
- On viewports up to 600px wide, the image fills 100% of the viewport.
- On viewports between 601px and 1000px, the image fills 50% of the viewport.
- On larger viewports, the image is displayed at a fixed 600px width.
Correct — pixel density descriptors (no sizes needed)
<img
src="logo-1x.png"
srcset="logo-1x.png 1x, logo-2x.png 2x"
alt="Company logo">
When using pixel density descriptors (1x, 2x) instead of width descriptors, the sizes attribute is not required. The browser simply matches the descriptor to the device’s pixel ratio.
Correct — using sizes with a <picture> element
<picture>
<source
srcset="hero-400.webp 400w, hero-800.webp 800w"
sizes="(max-width: 800px) 100vw, 800px"
type="image/webp">
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w, hero-800.jpg 800w"
sizes="(max-width: 800px) 100vw, 800px"
alt="Hero banner">
</picture>
The sizes attribute is required on both the source and img elements when either uses width descriptors in its srcset.
¿Listo para validar tus sitios?
Comienza tu prueba gratuita hoy.