Understanding Next.js Caching: How I Optimized App Using ISR, use cache & revalidateTag()

Search for a command to run...

No comments yet. Be the first to comment.
Large AI coding assistants are impressiveβbut once your project grows, they start spending a lot of time searching files, consuming tokens, and sometimes missing important context. I recently started

When developers hear "Cloudflare," they often think of a CDN or image hosting. That's exactly what I thought at first. But after digging deeper while building my own project, I realized Cloudflare is

Why I Chose Cloudflare R2 Recently, I was building a simple e-commerce application called ShopSphere. The application needed to store: Product images Category banners User-uploaded documents Init

How to Connect Neon Database to pgAdmin 4 (Quick Reminder) I always forget the exact steps when connecting my Neon Postgres database to pgAdmin, so I'm writing this as a personal reminder. Step-by-Ste

Akash Blog
15 posts
While building My Project, one of the biggest challenges wasn't building the UIβit was deciding how to cache data correctly.
I wanted my application to:
β‘ Feel instant while navigating
π Remain SEO-friendly
π Reduce database queries on Neon
π Show updated data immediately after an admin edits something
π Scale to thousands of users
After reading a lot of documentation and experimenting with different approaches, I finally settled on a hybrid caching strategy using ISR + Data Cache.
When I first started, I assumed there was only one cache in Next.js.
Turns out there are multiple layers.
Browser
β
βΌ
Route Cache (ISR)
β
βΌ
Data Cache (use cache)
β
βΌ
Neon PostgreSQL
Each layer has its own responsibility.
| Layer | Purpose |
|---|---|
| ISR (Incremental Static Regeneration) | Caches the generated HTML/RSC payload |
use cache |
Caches expensive database queries |
| Router Cache | Makes client-side navigation feel instant |
| Browser Cache | Stores static assets like JS, CSS and images |
π€ User
β
βΌ
Next.js Route (ISR)
β
HTML Already Cached?
β β
Yes No
β βΌ
β Server Component
β β
β βΌ
β use cache()
β β
β Cache Hit?
β β β
β Yes No
β β βΌ
β β Neon PostgreSQL
β β β
β βββββββββ
β
βΌ
Return HTML
This means that in most cases:
The page never reaches the database.
If ISR misses, the Data Cache still protects the database.
Even if both caches miss, only then does Neon receive a query.
For expensive database queries I use:
'use cache'
cacheLife('24h')
cacheTag(`restaurant:${id}`)
cacheTag('restaurants')
I intentionally chose a 24-hour TTL.
Why?
Because TTL is only a fallback.
The real freshness comes from tag invalidation.
Whenever an admin edits a restaurant:
await prisma.restaurant.update(...)
revalidateTag(`restaurant:${id}`)
revalidateTag('restaurants')
The cache is immediately invalidated.
The next request automatically fetches fresh data and recreates the cache.
Admin Updates Restaurant
β
βΌ
Database Updated
β
βΌ
revalidateTag()
β
βΌ
Cache Removed
β
βΌ
Next Visitor
β
βΌ
Fresh Database Query
β
βΌ
New Cache Created
Because of this workflow, I don't need a short cache TTL like 5 or 10 minutes.
Initially, I thought I should remove ISR completely.
After experimenting, I decided to keep it.
ISR helps by caching the rendered HTML, meaning many visitors can receive a ready-to-serve page without triggering a server render.
Even if ISR expires, the page regeneration usually doesn't hit the database because the Data Cache is still valid.
So the two caches work together rather than replacing each other.
One interesting thing I learned is that revalidateTag() only invalidates the cache on the server where it's executed.
Imagine three application servers:
Load Balancer
β
ββββββββββββββββΌβββββββββββββββ
βΌ βΌ βΌ
Server A Server B Server C
If the update happens on Server A:
Server A β
Fresh
Server B β Old Cache
Server C β Old Cache
To synchronize cache invalidation across every server, a shared system like Redis Pub/Sub or another distributed cache becomes useful.
That's something I plan to add as the application scales.
SWR is fantastic for:
Notifications
Dashboards
User profiles
Live data
Reviews
However, restaurant information changes relatively infrequently.
Using Server Components + ISR + Data Cache keeps the architecture simple while maintaining excellent SEO and minimizing unnecessary client-side requests.
β‘ Next.js App Router
βοΈ React Server Components (RSC)
ποΈ ISR (Incremental Static Regeneration)
πΎ use cache
π·οΈ cacheTag()
π revalidateTag()
π Neon PostgreSQL
βοΈ Netlify
π¦ Cloudflare R2 + CDN
π Redis (for multi-server cache synchronization)
One thing this journey taught me is that not every cache solves the same problem.
ISR makes page delivery faster.
use cache protects the database.
revalidateTag() keeps data fresh without waiting for TTLs.
Redis becomes valuable once multiple servers need to share cache invalidation.
Instead of trying to cache everything, understanding which layer should cache what made the biggest difference in both performance and simplicity.