The Ultimate Guide to Next.js Caching (Part 2): Cache Invalidation, SWR, React Query & Real Production Architecture

Search for a command to run...

No comments yet. Be the first to comment.
Meta Description: Learn how to convert your Next.js website into an Android app using Capacitor on Windows. Step-by-step guide with Android Studio setup, APK generation, and deployment. If you've alre

When someone opens your website, does Next.js immediately query your database? Not always. A request may pass through multiple cache layers before your database is even touched. The more requests serv

Introduction If you've been building with React or Next.js, you've probably heard terms like: SSR SSG ISR CSR PPR React Server Components unstable_cache At first, they all sound like differen

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

Akash Blog
19 posts
This is Part 2 of my Next.js Caching series. In Part 1, we covered Browser Cache, CDN, Router Cache, Full Route Cache, Data Cache,
unstable_cache, anduse cache.
We learned that a request flows through multiple cache layers:
Browser
│
Browser Cache
│
Cloudflare / Vercel CDN
│
Router Cache
│
Full Route Cache
│
Data Cache
│
unstable_cache / use cache
│
Database
But one important question remains...
How does the cache know when data has changed?
That's where cache invalidation comes in.
Imagine someone edits a restaurant menu.
Without invalidation:
Admin
↓
Database Updated
↓
Cache Still Old ❌
Visitors continue seeing outdated data.
Instead we want:
Admin
↓
Database Updated
↓
Invalidate Cache
↓
Fresh Data ✅
revalidateTag()The most common way to invalidate cached data.
When caching:
import { unstable_cache } from "next/cache";
export const getRestaurants = unstable_cache(
async () => prisma.restaurant.findMany(),
["restaurants"],
{
tags: ["restaurants"]
}
);
After updating data:
import { revalidateTag } from "next/cache";
revalidateTag("restaurants");
Everything using that tag becomes stale and is regenerated on the next request.
Perfect for:
CMS updates
Admin dashboards
CRUD operations
Menu updates
Product edits
revalidatePath()Sometimes you only want to refresh one page.
Example:
revalidatePath("/restaurants");
Useful when:
One page changes
Blog published
Product edited
Dashboard refreshed
Unlike revalidateTag, this targets a specific route.
updateTag()updateTag() immediately expires cached data without waiting for the next background revalidation.
updateTag("restaurants");
Think of it as:
revalidateTag()
↓
Refresh Soon
vs
updateTag()
↓
Refresh Immediately
Use it when users should instantly see fresh content after an action.
Not everything needs manual invalidation.
You can simply let the cache expire.
Example:
await fetch(API_URL, {
next: {
revalidate: 300
}
});
Flow:
Generate
↓
Cache
↓
5 Minutes
↓
Next Request
↓
Background Refresh
Great for:
Blogs
News
Product listings
Restaurant menus
CMS websites
| Situation | Recommendation |
|---|---|
| CMS update | revalidateTag() |
| Single page | revalidatePath() |
| Immediate refresh | updateTag() |
| Time-based updates | revalidate |
Some APIs automatically make a page dynamic.
Examples:
cookies()
headers()
draftMode()
searchParams
If your page depends on user-specific information, Next.js cannot safely serve the same cached page to everyone.
Example:
import { cookies } from "next/headers";
const token = cookies().get("token");
Now the page is personalized and won't benefit from full static caching.
Use these APIs only when needed.
Everything we've discussed so far happens before the page reaches the browser.
Once the page is loaded, SWR becomes another cache layer.
Database
▲
unstable_cache
▲
Data Cache
▲
CDN
▲
Browser
▲
SWR
Unlike Next.js caches, SWR runs completely on the client.
Example:
import useSWR from "swr";
const { data } = useSWR("/api/restaurants", fetcher);
Instead of fetching repeatedly:
SWR caches responses in memory.
Deduplicates requests.
Automatically revalidates.
Supports optimistic UI updates.
Good use cases:
Notifications
User profile
Reviews
Shopping cart
Live dashboards
Analytics
Avoid using SWR for:
Landing pages
Blogs
Marketing pages
SEO-critical pages
Those are better served using SSR, ISR, or SSG.
Database
▲
unstable_cache
▲
Data Cache
▲
Full Route Cache
▲
Cloudflare CDN
▲
Browser
▲
SWR
SWR is the last cache, not the first.
Think of it as the cache your React application uses after everything else has finished.
Both libraries solve the same problem: client-side server state management.
| Feature | SWR | React Query (TanStack Query) |
|---|---|---|
| Learning Curve | ⭐ Easy | ⭐⭐⭐ Medium |
| Bundle Size | Small | Larger |
| Caching | ✅ | ✅ |
| Background Revalidation | ✅ | ✅ |
| Optimistic Updates | Basic | Excellent |
| Infinite Queries | Basic | Excellent |
| Pagination | Basic | Excellent |
| DevTools | Limited | Excellent |
Use SWR if:
You're already using Next.js App Router
You only need simple client-side fetching
You want minimal setup
Choose React Query if:
Your app has complex CRUD operations
Heavy mutations
Infinite scrolling
Offline support
Complex cache management
This is the caching architecture I use in production.
User
│
Browser Cache
│
Cloudflare DNS
│
Cloudflare CDN (Images)
│
Vercel Edge
│
Next.js App
│
┌────────────┴─────────────┐
│ │
Static / ISR Pages Dynamic Pages
│ │
Full Route Cache unstable_cache
│ │
│ Prisma ORM
│ │
└────────────┬─────────────┘
│
Neon PostgreSQL
Images
User
│
Cloudflare CDN
│
Cloudflare R2
Images are served directly from:
Cloudflare CDN
↓
Cloudflare R2
Benefits:
Near-zero latency
Infinite free egress (R2)
No Next.js server execution
Reduced Vercel bandwidth usage
Restaurant pages use:
ISR
Full Route Cache
unstable_cache
Flow:
User
↓
Full Route Cache
↓
Data Cache
↓
unstable_cache
↓
Prisma
↓
Neon
If the data is cached, Prisma never runs.
Suppose an admin edits a menu.
Admin
↓
Update Database
↓
revalidateTag("restaurants")
↓
Cache Cleared
↓
Next Request
↓
Fresh Data
Only affected pages regenerate.
Everything else stays cached.
For authenticated pages, I avoid Full Route Cache.
Instead:
SSR
+
SWR
Reason:
Every user sees different data.
Caching the entire page wouldn't make sense.
Need SEO?
├── No
│ │
│ └── CSR + SWR
│
└── Yes
│
├── Never Changes
│ │
│ └── SSG
│
├── Changes Every Few Minutes
│ │
│ └── ISR
│
├── User Specific?
│ │
│ └── SSR
│
└── Expensive Database Query?
│
└── unstable_cache / use cache
fetch() inside unstable_cacheNext.js already caches fetch().
Adding another cache usually isn't needed.
no-store EverywhereMany developers disable caching while debugging and forget to remove it.
This forces every request to hit your server and database.
Updating the database doesn't automatically refresh cached pages.
Use:
revalidateTag()
revalidatePath()
updateTag()
when content changes.
Example:
revalidate: 5
Refreshing every five seconds often defeats the purpose of caching.
Choose a TTL based on how frequently your data actually changes.
Never cache content that depends on:
Cookies
Authentication
User preferences
Each user should receive their own response.
✔ Cache expensive database queries.
✔ Use unstable_cache (or use cache) for Prisma and ORM calls.
✔ Cache images at the CDN.
✔ Prefer tag invalidation over tiny TTL values.
✔ Let Cloudflare and Vercel serve as much content as possible.
✔ Measure before optimizing.
| Cache | Stores | Best For |
|---|---|---|
| Browser Cache | CSS, JS, Images | Static assets |
| Cloudflare CDN | Static assets | Global delivery |
| Router Cache | Navigation | Instant page transitions |
| Full Route Cache | HTML | SSG & ISR pages |
| Data Cache | fetch() responses |
APIs & CMS |
unstable_cache / use cache |
Database queries | Prisma, Drizzle, SQL |
| SWR | Client state | Dashboards & live data |
| React Query | Complex client state | Enterprise apps |
One thing I've learned while building production applications is this:
The fastest query isn't the optimized query—it's the one that never runs.
Next.js gives us multiple caching layers, each solving a different problem.
A well-optimized application usually combines several of them:
Browser Cache
Cloudflare CDN
Vercel Edge Cache
Full Route Cache
Data Cache
unstable_cache / use cache
SWR (or React Query)
The goal isn't to cache everything.
It's to cache the right thing at the right layer.
Once you start thinking in cache layers instead of individual queries, building fast and scalable applications becomes much easier.
Happy building! 🚀
📚 Next.js Caching
https://nextjs.org/docs/app/guides/caching
📚 unstable_cache
https://nextjs.org/docs/app/api-reference/functions/unstable\_cache
📚 use cache
https://nextjs.org/docs/app/api-reference/directives/use-cache
📚 SWR
📚 TanStack Query
https://tanstack.com/query/latest
The Ultimate Guide to Next.js Caching
Next.js Caching Explained with Real Production Examples
Browser Cache vs Data Cache vs unstable_cache
Mastering Next.js Cache Invalidation
How I Cache Production Next.js Apps
Learn how modern Next.js caching works with Browser Cache, Cloudflare CDN, Vercel, Full Route Cache, Data Cache, unstable_cache, use cache, SWR, React Query, and production-ready cache invalidation strategies.
nextjs react typescript performance webdev caching cloudflare vercel prisma softwareengineering
"Premium minimalist white-themed 16:9 tech illustration showing the complete Next.js caching pipeline. Visualize Browser Cache → Cloudflare CDN → Vercel Edge → Router Cache → Full Route Cache → Data Cache → unstable_cache/use cache → Prisma → PostgreSQL, with glowing blue and purple gradients, modern isometric layers, elegant typography reading 'The Ultimate Guide to Next.js Caching – Part 2', clean Apple-inspired design, subtle glassmorphism, futuristic developer aesthetic, digital art, no clutter."