How to Fix: Image Component flicker when refresh the page
Next.js Image Component Flicker on Page Refresh: Root Cause and Fix
The flicker is not your imagination. In development mode, the Next.js Image component can briefly repaint or swap states during a hard refresh, especially in the official reproduction example. The issue usually appears as a visible flash before the optimized image settles, and it is most noticeable when the page reloads, Fast Refresh reconnects, or layout calculations rerun.
Understanding the Root Cause
This behavior typically happens because the <Image /> component does more work than a plain <img> tag. On refresh, Next.js may need to:
- recreate the image wrapper and sizing logic,
- recalculate intrinsic dimensions or responsive layout,
- request the optimized image URL through the image optimizer,
- swap placeholder state to the final rendered asset,
- rerender during next dev because development tooling is intentionally less stable than production rendering.
In practice, the flicker often comes from a combination of these factors:
- Development mode rendering differences: next dev includes extra checks, Hot Reload, and React development behavior that can trigger more noticeable repaints than next build plus next start.
- Placeholder-to-final-image transition: if a blur placeholder or loading state is involved, the browser may briefly show one visual state before the final asset is decoded.
- Missing stable dimensions: when the browser cannot fully reserve space or the parent container changes during hydration, image paint can look like flicker even if there is no true layout shift.
- Hydration and client reconciliation: the server-rendered markup and the client-rendered tree may momentarily differ during refresh, especially around responsive image behavior.
- Image decoding timing: the optimized source may arrive quickly, but the visual replacement can still happen in a visible frame boundary.
The key point is that this is frequently a development-mode artifact, not always a production defect. Before changing code aggressively, confirm whether the issue reproduces in a production build.
Step-by-Step Solution
The most reliable fix is to make image rendering as stable as possible and verify whether the problem exists outside development mode.
1. Confirm whether the flicker only happens in development
Run a production build locally:
npm run build
npm run start
If the flicker disappears, the issue is primarily tied to next dev. That means your application may already be safe for users in production.
2. Always provide stable image dimensions
If you use the Image component with fixed-size rendering, provide width and height explicitly so the browser reserves layout space immediately.
import Image from 'next/image'
export default function Avatar() {
return (
<Image
src="/profile.jpg"
alt="Profile"
width={600}
height={400}
priority
/>
)
}
This reduces repaint risk and prevents the wrapper from changing size during refresh.
3. Use priority for above-the-fold images
If the image is visible immediately after page load, mark it as priority. This tells Next.js to preload it and reduces the chance of a delayed visual swap.
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={800}
priority
/>
This is especially effective for hero banners, logos, and main content images near the top of the page.
4. Avoid unstable parent layouts
If the image sits inside a container that changes size on hydration, the browser may repaint the image even when the source itself is correct. Keep the parent container stable.
export default function Hero() {
return (
<section>
<div>
<Image
src="/hero.jpg"
alt="Landing page hero"
width={1200}
height={800}
priority
/>
</div>
</section>
)
}
If you use fill, make sure the parent has predictable dimensions before hydration.
import Image from 'next/image'
export default function Banner() {
return (
<div style="position: relative; aspect-ratio: 16 / 9;">
<Image
src="/banner.jpg"
alt="Banner"
fill
priority
sizes="100vw"
/>
</div>
)
}
5. Test without blur placeholders if you see a visible transition
The blur-up effect can look like flicker on refresh in some setups. Remove it temporarily and compare behavior.
<Image
src="/photo.jpg"
alt="Photo"
width={800}
height={600}
priority
/>
If you were using:
<Image
src="/photo.jpg"
alt="Photo"
width={800}
height={600}
placeholder="blur"
blurDataURL="data:image/..."
/>
try disabling placeholder=”blur” and verify whether the flicker is actually the placeholder transition.
6. Keep React rendering predictable
If the Image component is mounted conditionally, or its key changes on refresh, React will treat it as a fresh node and repaint it.
// Avoid unstable keys
<Image
key="stable-hero-image"
src="/hero.jpg"
alt="Hero"
width={1200}
height={800}
priority
/>
Avoid patterns like random keys, timestamp-based keys, or conditional wrappers that replace the entire image subtree.
7. Compare against a plain img tag for diagnosis
If you need to isolate whether the problem is in next/image specifically, compare it to a standard image element.
export default function DebugImage() {
return (
<img
src="/hero.jpg"
alt="Hero"
width="1200"
height="800"
/>
)
}
If the plain <img> does not flicker but <Image /> does, the behavior is likely related to optimization, hydration, or placeholder logic rather than the asset itself.
8. Validate with the official example and current Next.js version
Because the issue was reported against the example project, test against the latest canary or stable release from the Next.js repository. Image behavior changes across releases, and some rendering bugs are fixed upstream.
npm install next@latest react@latest react-dom@latest
Then rerun the reproduction in both development and production modes.
Common Edge Cases
- Using fill without a stable parent: if the parent has no reliable height or aspect ratio, the image can repaint during hydration.
- CSS animations on load: fade-ins, transforms, or opacity transitions applied to the image or wrapper can look like framework flicker.
- Theme or breakpoint switches on hydration: if your layout changes after client-side state loads, images may re-render.
- Remote images: external loaders, caching headers, or image optimization latency can make the refresh flash more visible.
- Strict Mode in development: extra renders in React development workflows can amplify visual instability without affecting production.
- Placeholder mismatch: an incorrect blurDataURL or placeholder aspect ratio can create a noticeable swap.
- Unstable component trees: rendering the image inside a client-only branch or after a state check can delay first paint and cause a flash.
FAQ
Is this a real production bug or just a development issue?
Often it is mainly a development-mode issue. Always test with next build and next start. If the flicker disappears there, users are unlikely to see the same behavior in production.
Does using priority fix the flicker completely?
Not always, but it helps significantly for above-the-fold assets. priority reduces load delay and can prevent a visible source swap, though it will not fix unstable parent layout or hydration mismatches by itself.
Should I replace next/image with img?
Only as a diagnostic step or last resort. The Next.js Image component provides optimization, responsive sizing, and better loading behavior. In most cases, stabilizing dimensions, removing unnecessary placeholder transitions, and validating production rendering are better solutions than removing next/image.
If you want the practical fix in one sentence: test in production mode first, then make the image layout deterministic with explicit dimensions or a stable fill container, and use priority for images visible on first paint.