When I first started using Next.js, I replaced every <img> with <Image /> because the documentation said it was "optimized."
It worked.
But I had one question:
What is it actually doing behind the scenes?
After digging into it, I realized the idea is much simpler than I expected.
Let's walk through it together.
The problem with a normal <img>
Imagine you have a beautiful travel photo.
taj-mahal.png
4000 × 3000
6 MB
You display it on your website like this:
<img src="/taj-mahal.png" />
When someone opens your page, the browser asks the server for that image.
Browser
│
▼
GET /taj-mahal.png
│
▼
Server sends 6 MB image
The browser downloads the entire file.
Now imagine your image is only shown inside a small card that's 300 pixels wide.
The browser still downloads the full 4000-pixel image.
That means you're downloading way more data than you actually need.
What would be the smarter approach?
Instead of sending the same image to everyone, what if we could do this?
For phones:
300 px image
For tablets:
600 px image
For desktops:
1000 px image
Everyone sees the same picture, but only downloads the size they actually need.
That is the main idea behind image optimization.
Enter Next.js <Image />
Instead of writing
<img src="/taj-mahal.png" />
we write
<Image
src="/taj-mahal.png"
width={500}
height={300}
/>
Looks almost the same.
But behind the scenes, something very different happens.
The first surprise
You might think the browser requests this:
GET /taj-mahal.png
It doesn't.
Instead, it requests something like this:
GET /_next/image?url=/taj-mahal.png&w=640&q=75
At first, this looked strange to me.
Why isn't it requesting the actual image?
Meet the Image Optimizer
That /_next/image URL is a special endpoint created by Next.js.
Instead of sending the original image directly, the request goes through the optimizer first.
Browser
│
▼
Next.js Image Optimizer
│
▼
Original Image
You can think of it as a smart middleman.
Before giving the browser the image, it asks:
- What size do you need?
- What quality should I use?
- Which image format does your browser support?
Then it prepares the best version.
What happens when the request arrives?
Suppose the browser sends:
GET /_next/image?url=/taj-mahal.png&w=640&q=75
Next.js now knows:
- Original image location
- Required width (640px)
- Image quality (75)
Now the real work begins.
Step 1 — Load the original image
Next.js finds the original image.
It might come from:
- your
publicfolder - an external URL
- cloud storage like S3
The optimizer first loads the original image into memory.
Step 2 — Resize it
Suppose the original image is
4000 px wide
But the browser only needs
640 px
There is no reason to send all 4000 pixels.
Next.js resizes the image to the requested width.
4000 px
↓
640 px
This alone saves a lot of bandwidth.
Step 3 — Compress it
Even after resizing, the image can often be made much smaller.
If the quality is set to 75, Next.js compresses it while trying to keep it looking almost identical.
For example:
400 KB
↓
90 KB
The user usually can't notice the difference, but their internet connection definitely can.
Step 4 — Choose a better format
Modern browsers support formats like:
- WebP
- AVIF
These formats are usually much smaller than PNG or JPEG.
When your browser makes a request, it also sends an Accept header that tells the server which image formats it understands.
For example:
Accept: image/avif,image/webp,image/*,*/*
If the browser supports WebP or AVIF, Next.js may convert the original PNG or JPEG into one of those formats before sending it back.
Original
PNG
↓
Optimized
WebP
The picture looks almost identical but downloads much faster.
Step 5 — Cache the result
Image optimization isn't free.
Resizing and compressing images takes CPU time.
So Next.js stores the optimized version.
The first request looks like this:
Browser
↓
Next.js
↓
Resize
↓
Compress
↓
Cache
↓
Send image
The second request becomes much simpler.
Browser
↓
Next.js
↓
Cache
↓
Send image
No resizing.
No compression.
Just return the cached image.
How does the browser know which size to download?
This part surprised me the most.
Next.js doesn't generate just one image.
It generates multiple versions.
Something like:
320 px
640 px
750 px
1080 px
1920 px
It then generates an HTML srcset attribute.
Something similar to this:
<img
src="/_next/image?url=/taj-mahal.png&w=640&q=75"
srcset="
/_next/image?url=/taj-mahal.png&w=320&q=75 320w,
/_next/image?url=/taj-mahal.png&w=640&q=75 640w,
/_next/image?url=/taj-mahal.png&w=1080&q=75 1080w
"
sizes="100vw"
/>
Now here's the interesting part.
Next.js doesn't decide which image gets downloaded.
The browser does.
If you're on a phone, it might download the 640px version.
If you're on a desktop with a Retina display, it might choose the 1080px version.
The browser looks at:
- Viewport size
- Device pixel ratio (Retina, 2x, 3x, etc.)
- The
sizesattribute
Then it picks the most appropriate image.
Why do we provide width and height?
One more nice feature.
When you write
<Image
width={800}
height={600}
/>
the browser already knows how much space the image will take.
Even before the image finishes downloading.
That means your page doesn't suddenly jump around while loading.
Without dimensions:
Header
Text
(Image loads)
Everything moves down
With dimensions:
Header
[Reserved Image Space]
Text
Everything stays stable.
This improves a Core Web Vitals metric called Cumulative Layout Shift (CLS).
Putting everything together
<Image />
│
▼
Browser requests
/_next/image
│
▼
Next.js loads the original image
│
▼
Resize
│
▼
Compress
│
▼
Convert to WebP/AVIF (if supported)
│
▼
Store in cache
│
▼
Send optimized image to the browser
Final thoughts
Before learning this, I thought <Image /> was just another React component.
It turns out it's much more than that.
It's an image optimization pipeline built into Next.js.
Instead of sending every visitor the same large image, it creates a version that's appropriate for their device, browser, and screen size.
As developers, we only write one component.
Behind the scenes, Next.js handles resizing, compression, modern image formats, caching, and responsive images for us.
That's a lot of work hidden behind a single line of code.
If you enjoyed this article, the next logical step is understanding how srcset and sizes actually work. That's where the browser starts making intelligent decisions about which image to download, and it's one of the coolest parts of responsive images.
Top comments (0)