Skip to content

fix(image): match Next.js 16 default image sizes - #2704

Merged
james-elicx merged 2 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/fix-next16-image-sizes
Jul 27, 2026
Merged

fix(image): match Next.js 16 default image sizes#2704
james-elicx merged 2 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/fix-next16-image-sizes

Conversation

@NathanDrake2406

Copy link
Copy Markdown
Contributor

Summary

  • Match Next.js 16's default images.imageSizes by removing 16
  • Reuse the shared server default when generating client image config
  • Keep explicit imageSizes configurations containing 16 supported

Why

vinext is pinned to Next.js 16.2.7, but still used the pre-v16 default:

[16, 32, 48, 64, 96, 128, 256, 384]

Next.js 16 removed 16 because it is rarely requested and adds an unnecessary
candidate to responsive srcset output. The mismatch also meant vinext's image
optimizer accepted a default width that Next.js 16 no longer accepts.

Behavior

Scenario Before After
Default 16px fixed image Emits 16px and 32px candidates Both targets snap to one 32px candidate
Default optimizer request for w=16 Accepted Rejected
Explicit images.imageSizes containing 16 Accepted Unchanged

Validation

  • vp test run tests/image-component.test.ts tests/shims.test.ts
    • 1,322 tests passed
  • vp check packages/vinext/src/config/next-config.ts packages/vinext/src/index.ts packages/vinext/src/server/image-optimization.ts packages/vinext/src/shims/image.tsx tests/image-component.test.ts tests/shims.test.ts
    • Formatting, lint, and types passed for all six files
  • node scripts/check-shim-types.mjs
    • 24 public shim modules matched their vendored types
  • Pre-commit full check, staged tests, and knip passed

Risk

This is an intentional Next.js 16 compatibility change for projects that rely
on the default image sizes. Projects that still require 16px optimization can
opt back in with images.imageSizes, matching Next.js's migration guidance.
Explicit configurations are otherwise unchanged.

References

Next.js 16 removed the 16px default, but vinext continued generating and accepting it when imageSizes was unset. This added an unnecessary srcSet candidate and diverged from the pinned Next.js behavior.

Use the shared server default for build-time defines and keep the client shim fallback aligned. Explicit imageSizes configurations that include 16px remain supported.
@NathanDrake2406
NathanDrake2406 marked this pull request as ready for review July 25, 2026 06:24
@pkg-pr-new

pkg-pr-new Bot commented Jul 25, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2704
npm i https://pkg.pr.new/create-vinext-app@2704
npm i https://pkg.pr.new/@vinext/types@2704
npm i https://pkg.pr.new/vinext@2704

commit: 6344808

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 6344808 against base 05eee9f using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 6 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 132.4 KB 132.4 KB ⚫ +0.0%
Client entry size (gzip) vinext 119.7 KB 119.7 KB ⚫ +0.0%
Dev server cold start vinext 2.84 s 2.83 s ⚫ -0.5%
Production build time vinext 3.13 s 3.13 s ⚫ +0.1%
RSC entry closure size (gzip) vinext 103.3 KB 103.3 KB ⚫ +0.0%
Server bundle size (gzip) vinext 177.8 KB 177.8 KB ⚫ -0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

The Next.js 16 default-width change correctly updated generated App Router entries, but this behavior test still encoded the previous list. Align the expectation while preserving the neighboring explicit 16px opt-in coverage.
@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Next.js 16 default image sizes

The core change is correct and well-verified. I confirmed against next.js@v16.2.7/packages/next/src/shared/lib/image-config.ts that imageConfigDefault.imageSizes is exactly [32, 48, 64, 96, 128, 256, 384]16 was removed. All five source/test locations that carried the old default are updated consistently:

  • DEFAULT_IMAGE_SIZES in image-optimization.ts (single source of truth)
  • index.ts now reuses DEFAULT_IMAGE_SIZES instead of a hardcoded literal (good dedup)
  • shim fallback in image.tsx (both the JSON.parse default string and the catch fallback)
  • JSDoc in next-config.ts
  • three test files

Behavior verification

I traced getImageWidths() in shims/image.tsx:324. For a 16px fixed image, targets are [16, 32]; the smallest configured width >= 16 is now 32 and >= 32 is 32, so both collapse to a single 32 candidate. The updated image-component.test.ts assertion (32 1x, no 16 candidate) correctly reflects this. The parseImageParams test changes (reject w=16, accept w=32/w=640 under defaults) also match the new allowlist.

The shims.test.ts case at line ~22596 still passes an explicit allowedWidths array containing 16, so w=16 is still accepted there — correctly preserving the "explicit config containing 16" path.

Non-blocking observations

No blocking issues. Two optional notes below. This change is a deliberate, documented Next.js 16 parity fix and looks ready to merge once CI is green.

@@ -2267,7 +2267,7 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] {
const deviceSizes = nextConfig.images?.deviceSizes ?? [
640, 750, 828, 1080, 1200, 1920, 2048, 3840,
];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional/pre-existing: since this block now reuses DEFAULT_IMAGE_SIZES for imageSizes (nice dedup), deviceSizes just above still uses a hardcoded literal while DEFAULT_DEVICE_SIZES is already imported (used at line 4902). Reusing it here too would keep both defaults in one place:

Suggested change
];
const deviceSizes = nextConfig.images?.deviceSizes ?? DEFAULT_DEVICE_SIZES;

Not required for this PR — just flagging the asymmetry now that the sibling line was cleaned up.

@ask-bonk

ask-bonk Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review posted successfully.

Summary

I reviewed PR #2704 (fix(image): match Next.js 16 default image sizes) and verified it is correct and complete.

Findings:

  • Core change verified against upstream: Fetched next.js@v16.2.7's image-config.ts and confirmed imageConfigDefault.imageSizes is exactly [32, 48, 64, 96, 128, 256, 384] — the 16 was removed in v16, matching this PR's change precisely.
  • All locations updated consistently: DEFAULT_IMAGE_SIZES (single source), the index.ts define generation (now reuses the constant instead of a hardcoded literal), the shim's JSON.parse default string and catch fallback, JSDoc, and three test files.
  • Behavior trace confirmed: For a 16px fixed image, getImageWidths() now collapses both 1x/2x targets to a single 32 candidate. The updated test assertions match this exactly.
  • Explicit-config path preserved: The shims.test.ts case passing an explicit allowedWidths containing 16 still accepts w=16.

One non-blocking suggestion posted inline: line 2267-2269 of index.ts still hardcodes deviceSizes while DEFAULT_DEVICE_SIZES is already imported — worth reusing for symmetry now that the sibling imageSizes line was cleaned up.

No blocking issues. The change is a deliberate, well-documented Next.js 16 parity fix.

github run

@james-elicx
james-elicx merged commit da0a8ef into cloudflare:main Jul 27, 2026
56 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants