Blog / 01

Next.js 16.3.4 / 15.5.25 Re-enable AVIF Optimization

WebRestart

On August 31, 2026 Vercel shipped 16.3.4 and 15.5.25, which undo the most disruptive part of the previous week's security release: AVIF image optimization works again. The two lines get there by different routes, and the difference matters. 15.5.25 checks the libheif version bundled with sharp at runtime and only decodes AVIF if that version is patched. 16.3.4 does not check anything — it relies entirely on the sharp version your install resolves.

If you upgraded to 16.3.3 and moved on, the practical consequence is this: on 16.3.4 the safety of AVIF decoding is a property of your lockfile, not of Next.js.

What was broken and why

The August 25 release fixed a Critical heap overflow in libheif reachable through the Image Optimization API. Because the patched libheif had not propagated down the sharp dependency chain yet, Vercel's fix took the blunt option: AVIF joined BYPASS_TYPES alongside SVG and ICO, VipsForeignLoadHeif was removed from sharp's unblocked operations, and AVIF fell out of VALID_BLUR_EXT. AVIF source images were passed through unoptimized and static AVIF imports lost their blur placeholders.

That trade was worth making at the time. It was never meant to be permanent.

16.3.4: a revert plus a dependency floor

PR #97949 is two commits. The first reverts the disable wholesale — BYPASS_TYPES goes back to [SVG, ICO, ICNS, BMP, JXL, HEIC], VipsForeignLoadHeif returns to the unblock list, and 'avif' is restored to the valid blur extensions in blur.ts, get-img-props.ts and image.tsx. Roughly 137 lines of workaround come out.

The second commit bumps sharp in packages/next/package.json from ^0.35.3 to ^0.35.4. That is the entire safety mechanism. The libheif fix ships inside sharp's prebuilt libvips binaries rather than in Next.js code, so the version floor on sharp is what separates a patched decoder from an unpatched one.

Read the shipped image-optimizer.ts on the v16.3.4 tag and there is no version guard: AVIF is optimized whenever sharp loads.

sharp is an optional dependency of next. A floor in Next.js's package.json constrains what a fresh install pulls. It does not upgrade a sharp you installed yourself, pinned in your own dependencies, or baked into a container image.

15.5.25: an actual runtime check

The 15.5 line could not take the same shortcut. PR #97954 explains why: "Since 15.x supports a Node.js version not supported by a secure sharp version, we have to conditionally block AVIF optimization based on the used libheif version." Its sharp range is ^0.34.3 || ^0.35.4, which deliberately admits an older major.

So 15.5.25 asks the library at startup. image-optimizer.ts gains an isAvifDecodeSafe() helper that parses sharp.versions.heif and returns true only for libheif 1.23.2 or newer — the version that carries the upstream fix:

if (isAvifDecodeSafe(heifVersion)) {
  sharp.unblock({ operation: ['VipsForeignLoadHeif'] })
}

Run 15.5.25 against an old sharp and AVIF stays bypassed. Run 16.3.4 against the same old sharp and it does not.

What to do

Upgrade, then verify the resolution rather than assuming it:

npm install next@16.3.4   # or next@15.5.25
npm ls sharp
node -e "console.log(require('sharp').versions)"

The second command tells you which sharp actually won; the third prints the bundled library versions, including heif. You want heif at 1.23.2 or above. If it is lower, either upgrade sharp or keep AVIF out of your optimizer's reach — images.formats controls output, but the decode path is entered by any AVIF source, including remote URLs matched by images.remotePatterns and user uploads.

Two follow-ups. If you set your own blurDataURL as a workaround for statically imported AVIF images losing their placeholder in 16.3.3, that workaround is now redundant but harmless. And if you run a custom Node.js image with a system-level sharp, this is the exact scenario the 16.3.x approach does not cover; pin sharp explicitly at >=0.35.4 in your own package.json so the constraint is yours rather than inherited.

16.3.4 also backports three unrelated fixes: infinite recursion in testmode passthrough fetch (#97691), build errors when aliasing TypeScript to @typescript/typescript6 (#97997), and unset crossOrigin attributes in Turbopack manifests (#97930). It explicitly does not include pending canary work — 16.4 is still in canary.

Primary sources: the v16.3.4 and v15.5.25 release tags, and PRs #97949 and #97954.


Not sure what sharp your production image is really resolving, or whether your image pipeline is still bypassing AVIF? Get in touch — auditing and upgrading Next.js deployments is what we do.