How to Fix: Next js 14 preloads images but 15 does not
Next.js 15 changed how image preloading is emitted, so code that appeared to preload correctly in Next.js 14 may no longer generate the same <link rel="preload" as="image"> behavior. In most cases, the image is still optimized and rendered, but the expected preload hint is missing because the newer rendering pipeline, stricter heuristics around priority, and App Router streaming behavior can prevent preload tags from being inserted where developers expect them.
Understanding the Root Cause
In Next.js 14, using the priority prop on the next/image component commonly resulted in a preload hint being added to the document head for above-the-fold images. In Next.js 15, several internal changes can affect that outcome:
- Updated rendering and streaming behavior: With App Router and server streaming, the framework may defer or restructure when head elements are emitted.
- Stricter preload heuristics: Next.js does not blindly preload every priority image. It may skip preloading if the image is not considered a strong LCP candidate or if the component is rendered in a context where preload injection is not guaranteed.
- Layout and visibility constraints: If the image is conditionally rendered, hidden, moved below the fold, or wrapped in a client boundary that delays rendering, preload hints may not be emitted.
- Version-specific regressions: The issue report strongly suggests a framework regression or behavioral change between versions rather than a user-land syntax error.
Put simply, priority in Next.js 15 is not always equivalent to “always emit a preload tag”. If your optimization strategy relied on verifying preload tags in the head, that assumption may break after upgrading.
Step-by-Step Solution
The safest fix is to validate that the image is a real above-the-fold asset, keep priority on the image, and if needed, add an explicit preload using the metadata/head layer or a manual head tag while monitoring framework updates.
1. Confirm the image uses next/image correctly
import Image from 'next/image'
export default function Hero() {
return (
<Image
src="/hero.jpg"
alt="Hero banner"
width={1600}
height={900}
priority
/>
)
}
This is still the correct baseline. Make sure the image is not being swapped out later by client-side state.
2. Ensure the image is rendered immediately on first paint
If the image is inside a delayed client component, tab panel, modal, carousel, or conditional branch, the framework may not treat it as preload-worthy.
import Image from 'next/image'
export default function Page() {
return (
<main>
<h1>Welcome</h1>
<Image
src="/hero.jpg"
alt="Hero banner"
width={1600}
height={900}
priority
/>
</main>
)
}
Avoid patterns like this for your supposed LCP image:
{showHero && (
<Image src="/hero.jpg" alt="Hero" width={1600} height={900} priority />
)}
3. Check whether you are using App Router and streamed server components
In Next.js 15, head management and preload emission can be affected by streaming boundaries. If your image is nested deep in asynchronously rendered server components, try moving the hero image higher in the route tree so it appears earlier during render.
import Image from 'next/image'
export default async function Page() {
return (
<>
<Image
src="/hero.jpg"
alt="Hero banner"
width={1600}
height={900}
priority
/>
{/* async content below */}
</>
)
}
4. Add an explicit preload as a workaround
If the framework does not emit the preload you need, add it manually. This is the most reliable short-term workaround when dealing with a version-specific regression.
export default function HeadPreload() {
return (
<>
<link
rel="preload"
as="image"
href="/hero.jpg"
/>
</>
)
}
If you are in the App Router, place the preload in the route head strategy your app uses, and keep the Image component itself unchanged.
5. Keep priority on the image even with manual preload
Manual preload helps the browser fetch early, but priority still communicates intent to Next.js image optimization and loading strategy.
import Image from 'next/image'
export default function Hero() {
return (
<>
<link rel="preload" as="image" href="/hero.jpg" />
<Image
src="/hero.jpg"
alt="Hero banner"
width={1600}
height={900}
priority
/>
</>
)
}
6. Verify in the browser, not just in source assumptions
Open DevTools and confirm:
- The network request for the hero image starts early.
- A preload entry exists in the Elements or Network panel if manually added.
- The image is actually the page’s LCP candidate.
If you do not see preload behavior after adding a manual link tag, the problem may be an incorrect asset path, CDN rewrite, or head placement issue.
7. Test against the latest patched release
Because this appears to be a framework behavior difference, upgrade to the latest Next.js 15.x patch and retest. If the bug persists, track the issue in the Next.js repository and compare release notes for image-related fixes.
npm install next@latest react@latest react-dom@latest
Common Edge Cases
- Remote images: If you load from an external domain, verify your image configuration allows that host. Otherwise optimization and preload behavior may differ.
- Conditional rendering: Images inside feature flags, client state, or lazy UI sections often miss preload injection.
- Multiple priority images: Marking too many images as priority can reduce the usefulness of preloading and may trigger framework heuristics that limit what gets hinted.
- Using CSS background images: The
next/imagepriority prop does nothing for CSS backgrounds. Those require separate preload handling. - Dynamic src values: If the final image URL depends on async data or client logic, the server may not emit a stable preload hint.
- Head placement mistakes: Manual preload tags only work if they are inserted into the actual document head path used by your routing setup.
FAQ
Does priority still work in Next.js 15?
Yes, but priority does not always guarantee the same visible preload output you may have seen in Next.js 14. It still signals that the image is important, but emitted preload behavior can vary.
Should I remove priority and only use a manual preload?
No. Keep priority on genuine above-the-fold images. Use a manual preload only as a workaround when the framework does not emit the hint you need.
How do I know whether this is my code or a framework regression?
If the same component structure preloads correctly in Next.js 14 but not in Next.js 15, and the image is rendered immediately with a valid next/image setup, it strongly points to a framework behavior change or regression. Validate with a minimal reproduction and compare both versions side by side.
Recommended fix: keep the image above the fold, render it on first paint, preserve the priority prop, and add an explicit preload tag if you need deterministic behavior until the framework behavior is clarified or patched.