
I run amankumar.ai on Next.js. Pages would load, layouts would appear, and React would do its thing, but something still felt slow.
The problem wasn’t that the page didn’t render.
The problem was this:
The page renders, but images stay blank for a moment. Then they drop in one by one. And the site doesn’t feel visually complete until later.
That gap, where placeholders sit there and images arrive late, is subtle, but once you notice it, you can’t unsee it. It makes the site feel heavier than it should.
So I stopped guessing and looked at the boring part I’d been ignoring: image payloads.
This problem shows up a lot more today, even on well-built sites, and it’s not because people are careless.
A few things are happening at once:
The result is exactly what I was seeing: the page loads, but images arrive last.
Before touching anything, I measured the image assets in the repo.
They were a mix of:
After running a single optimization pass, the numbers looked like this:
| Metric | Before | After |
|---|---|---|
| Total image size | 83.57 MB | 1.89 MB |
| Reduction | 97.7% | |
| Avg per image | 690 KB | 15.5 KB |
| Space saved | 81.68 MB |
This is repo-level image weight, not “every page ships 83 MB.” But it clearly showed the root issue. I was carrying a lot of unnecessary image data, and the browser was paying for it.
I didn’t want a clever setup. I wanted something:
So the real question became:
What’s the simplest image pipeline that’s correct most of the time?
Before debating PNG vs WebP vs AVIF, the biggest issue was pixel count.
If an image is 2400px wide but is never rendered above ~800px, shipping the extra pixels is pure waste.
So I made one hard rule:
Cap the max dimension at 800px. If an image is larger, downscale it. If it’s smaller, leave it alone.
This single decision removes a surprising amount of weight.
Once dimensions are sane, format choice actually matters.
Here’s the practical ranking when comparing images at roughly the same visual quality:
| Format | Visual Quality | File Size | Notes |
|---|---|---|---|
| AVIF | Excellent | Smallest | Best compression, newer |
| WebP | Excellent | Very small | Mature, predictable |
| JPEG | Good | Medium | Older, lossy |
| PNG | Perfect (lossless) | Huge | Often misused |
AVIF genuinely compresses better than WebP in many cases.
This is important, because AVIF is genuinely impressive.
The reason I didn’t standardize on it is not quality. It’s practical browser support and operational safety.
According to current browser compatibility data:
You can see the current state clearly here: https://caniuse.com/?search=avif
For this project, I didn’t want:
So the decision wasn’t “AVIF is bad.”
It was:
WebP is the safest modern default today. AVIF can be layered later if needed.
My repo wasn’t just photos.
It had:
If you treat everything like a photo and apply lossy compression everywhere, UI assets suffer:
WebP helped here because it supports both lossy and lossless modes.
The challenge was choosing between them without manual tagging.
I used one clean signal:
Is this perfect? No. Is it correct often enough to automate safely? Yes.
That single rule handled mixed assets without human intervention.
Many images carry metadata:
None of this helps a webpage load faster or look better.
So I strip metadata unconditionally. Sometimes the savings are small; sometimes they’re large. Either way, it’s free.
To make sure this wasn’t a one-off, I ran the same script, unchanged, on another repo: promptsmint.com, an AI prompts library with only AI-generated images.
Before optimization
After optimization
Result
Different site. Different content. Same outcome.
That confirmed this wasn’t luck. It was just removing waste.
I packaged the whole pipeline into a single script and hosted it here:
https://gist.github.com/onlyoneaman/cb5dbd36ed351b02e46db13d74e6dbe2
It:
No clever tricks. Just consistent rules.
I didn’t magically make Next.js faster.
What changed is simpler and more important:
Images arrive earlier. Placeholders disappear sooner. Pages feel visually complete faster.
That was the slowness I was noticing, and this fixed it.
Thanks for reading. If you found this useful, you can follow or connect with me here: