payloadcms / payloadcms/payload

Sizes attribute in ImageMedia uses w instead of px - causes browser to ignore all breakpoint hints

Open
#16,751 1 comment 0 reactions 1 assignee View on GitHub

@paulpopus is already working on this.

Since Jun 3, 2026.

area: templates invalid-reproduction v3
Dominant language
TypeScript
Stars
44.8k
Forks
4.2k
Avg merge
2d 21h
Merged PRs (30d)
53

Description

Describe the Bug

Hey! Found a bug in the ImageMedia component that affects both templates/website and templates/with-vercel-website.

The default sizes string is built like this:

Object.entries(breakpoints)
  .map(([, value]) => `(max-width: ${value}px) ${value * 2}w`)
  .join(', ')

The problem is the w unit, that's a srcset width descriptor and is only valid inside srcset, not sizes. The sizes attribute expects CSS length units like px, vw, em, etc. (MDN
(https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/sizes), HTML spec (https://html.spec.whatwg.org/multipage/images.html)).

Browsers silently discard the invalid values and fall back to 100vw, so the entire breakpoint calculation does nothing in practice. Since Next.js relies on sizes to decide which srcset entry to fetch, all
images end up downloading a full-viewport-width variant regardless of their actual layout size.

The fix is straightforward, use px and drop the * 2 (Next.js handles retina/DPR automatically via srcset, so doubling the hint works against you):

Object.entries(breakpoints)
  .map(([, value]) => `(max-width: ${value}px) ${value}px`)
  .join(', ')

Interestingly, the ecommerce template already does this correctly in templates/ecommerce/src/components/Media/Image/index.tsx, so it's just the two website templates that have the bug.

Link to the code that reproduces this issue

pnpx create-payload-app@latest -t website

Reproduction Steps

https://github.com/payloadcms/payload

Just run pnpx create-payload-app@latest -t website and observe issue in src/components/Media/ImageMedia/index.tsx

Which area(s) are affected?

area: templates

Environment Info
Binaries:
  Node: 25.0.0
  npm: 11.6.2
  Yarn: N/A
  pnpm: 10.28.2
Relevant Packages:
  payload: 3.84.1
  next: 16.2.6
  @payloadcms/db-vercel-postgres: 3.84.1
  @payloadcms/drizzle: 3.84.1
  @payloadcms/graphql: 3.84.1
  @payloadcms/live-preview: 3.84.1
  @payloadcms/live-preview-react: 3.84.1
  @payloadcms/next/utilities: 3.84.1
  @payloadcms/plugin-cloud-storage: 3.84.1
  @payloadcms/plugin-form-builder: 3.84.1
  @payloadcms/plugin-nested-docs: 3.84.1
  @payloadcms/plugin-redirects: 3.84.1
  @payloadcms/plugin-search: 3.84.1
  @payloadcms/plugin-seo: 3.84.1
  @payloadcms/richtext-lexical: 3.84.1
  @payloadcms/storage-vercel-blob: 3.84.1
  @payloadcms/translations: 3.84.1
  @payloadcms/ui/shared: 3.84.1
  react: 19.2.1
  react-dom: 19.2.1
Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Fri, 01 May 2026 15:49:22 +0000
  Available memory (MB): 31724
  Available CPU cores: 12

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.