How to Fix: Next.js Image component always loads image as 3840px
The Next.js Image component is not actually “ignoring” your image size when it requests 3840px; it is following its responsive image generation rules because the browser is being told that the image may need a very large source. In development, this often becomes more visible because image optimization behavior is easier to inspect and the generated srcset candidates include large widths.
Understanding the Root Cause
This bug usually happens when a responsive image is rendered without a precise enough sizes value, or when the layout mode allows the browser to assume the image could span a large portion of the viewport. The Next.js <Image /> component generates a srcset using widths from your configured image breakpoints. When the browser evaluates that srcset, it chooses the candidate that best matches the rendered size multiplied by the device pixel ratio.
If the image is using fill, a CSS layout that stretches, or a missing/misleading sizes attribute, the browser may conclude that it needs a much larger image candidate. On large screens or high-density displays, that can easily resolve to 3840px.
There are three technical reasons this happens most often:
- Missing sizes: Without a realistic sizes value, the browser may assume a wider rendered image than intended.
- Layout-driven expansion: Parent containers using full-width or unconstrained sizing can make the image appear eligible for larger source candidates.
- Device pixel ratio: A visually small image can still request a large asset on Retina or other high-DPI screens because the browser wants enough physical pixels.
In short, the issue is not that Next.js always loads 3840px images. The issue is that the image configuration and layout are causing the browser to select the 3840px srcset candidate.
Step-by-Step Solution
The fix is to make the intended rendered width explicit. The most reliable approach is to define accurate sizes, use constrained layout rules, and avoid using fill unless the parent box dimensions are deliberate.
1. Inspect the current Image usage
If your component looks something like this, the browser may choose a larger candidate than expected:
import Image from 'next/image'
export default function Page() {
return (
<Image
src="/hero.png"
alt="Example"
fill
/>
)
}
With fill, the image size depends entirely on the parent container and the sizes hint. If sizes is missing or too broad, large downloads are expected.
2. Add an accurate sizes attribute
Tell the browser exactly how wide the image will be at different breakpoints:
import Image from 'next/image'
export default function Page() {
return (
<div style="position: relative; aspect-ratio: 16 / 9; max-width: 100%;">
<Image
src="/hero.png"
alt="Example"
fill
sizes="(max-width: 768px) 100vw, 50vw"
/>
</div>
)
}
This example says:
- On screens up to 768px wide, the image takes 100vw.
- On larger screens, it takes about 50vw.
That gives the browser a realistic target and reduces unnecessary large image selections.
3. If the image has a fixed rendered size, use width and height directly
If the image does not need full responsive fill behavior, the simplest fix is to avoid fill entirely:
import Image from 'next/image'
export default function Avatar() {
return (
<Image
src="/avatar.png"
alt="User avatar"
width={320}
height={320}
sizes="320px"
/>
)
}
This is ideal when the rendered dimensions are known. It prevents the browser from considering huge candidates for a small visual element.
4. Constrain the parent container when using fill
When using fill, always ensure the wrapper has intentional dimensions:
import Image from 'next/image'
export default function CardImage() {
return (
<div style="position: relative; width: 100%; max-width: 50%; aspect-ratio: 4 / 3;">
<Image
src="/photo.jpg"
alt="Card photo"
fill
sizes="(max-width: 900px) 100vw, 50vw"
/>
</div>
)
}
If the parent is effectively full-screen, the browser behaves correctly by picking a large image candidate.
5. Verify your next.config.js image settings
Next.js uses configured device sizes and image sizes to build optimization candidates. If your config includes very large widths, those become available in the generated srcset.
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
deviceSizes: [640, 750, 828, 1080, 1200, 1920],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384]
}
}
module.exports = nextConfig
If 3840 is showing up because your configuration exposes extremely large sizes you do not need, reducing those breakpoints can help. Do this carefully, because overly restrictive values can hurt quality on larger screens.
6. Test in production mode too
Development mode can be misleading when debugging image behavior. Always validate with a production build:
npm run build
npm run start
Then inspect the rendered img, its srcset, and the chosen network request again.
Recommended final pattern
import Image from 'next/image'
export default function Home() {
return (
<section>
<div style="position: relative; width: 100%; aspect-ratio: 16 / 9;">
<Image
src="/banner.jpg"
alt="Banner"
fill
priority
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 60vw"
/>
</div>
</section>
)
}
This pattern works because it combines:
- A bounded parent container
- An explicit sizes hint
- A responsive strategy aligned with actual layout
Common Edge Cases
- High-DPI displays: Even after fixing sizes, the browser may still choose a larger asset than the CSS width because of a 2x or 3x pixel ratio.
- Incorrect CSS on parent elements: A wrapper with unexpected full-width behavior, grid stretching, or flex growth can make the browser compute a larger rendered size.
- Using fill without height context: If the container has no stable dimensions, the image layout may behave unpredictably.
- Overly broad sizes values: Values like 100vw for an image that only occupies a card column will encourage larger downloads.
- Custom image config: Large deviceSizes arrays can expose unnecessarily large optimization candidates.
- Misreading DevTools: Seeing 3840 in the requested URL does not always mean the image is rendered at 3840 CSS pixels. It means that candidate was selected from the available optimized widths.
FAQ
Why does Next.js request 3840px for a visually smaller image?
Because the browser chooses from the generated srcset based on the computed display size and device pixel ratio. If your sizes hint is missing or too large, 3840 can be selected even when the image appears smaller in CSS pixels.
Is this only a development mode issue?
No. It can happen in both development and production. Development mode just makes it easier to notice. You should still verify the behavior with a production build because optimization details can differ.
Should I remove fill to fix this?
Not necessarily. fill is fine when the parent container is well-defined and the sizes attribute accurately describes the real layout. If the image has a fixed or predictable size, using width and height is often simpler.
The practical fix is to stop letting the browser guess. Give Next.js Image a realistic layout context, add the right sizes value, and constrain the container so the chosen source matches the image users actually see.