Skip to main content

Command Palette

Search for a command to run...

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

Updated
β€’9 min readβ€’View as Markdown
The Ultimate Guide to Next.js Caching (Part 2): Cache Invalidation, SWR, React Query & Real Production Architecture
A
πŸ€– Curious AI enthusiast and Software Engineer with 7+ years of experience. πŸ› οΈ I enjoy getting my hands dirty with new technologies β€” experimenting, building side projects, and learning by doing. πŸš€ Currently exploring how AI can enhance real-world web applications.

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, and use cache.


Previously...

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.


Why Cache Invalidation Matters

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.


Time-Based Revalidation

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


Which Should You Use?

Situation Recommendation
CMS update revalidateTag()
Single page revalidatePath()
Immediate refresh updateTag()
Time-based updates revalidate

Dynamic Rendering Disables Caching

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.


Where Does SWR Fit?

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.


When Should You Use SWR?

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.


Cache Flow with SWR

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.


SWR vs React Query

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

My Recommendation

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


How I Cache a Production Next.js App

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

Why this architecture?

Images

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 List

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.


Admin Updates

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.


User Dashboard

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.


Which Cache Should I Use?

Need SEO?

β”œβ”€β”€ No
β”‚      β”‚
β”‚      └── CSR + SWR
β”‚
└── Yes
       β”‚
       β”œβ”€β”€ Never Changes
       β”‚      β”‚
       β”‚      └── SSG
       β”‚
       β”œβ”€β”€ Changes Every Few Minutes
       β”‚      β”‚
       β”‚      └── ISR
       β”‚
       β”œβ”€β”€ User Specific?
       β”‚      β”‚
       β”‚      └── SSR
       β”‚
       └── Expensive Database Query?
              β”‚
              └── unstable_cache / use cache

Common Caching Mistakes

❌ Wrapping fetch() inside unstable_cache

Next.js already caches fetch().

Adding another cache usually isn't needed.


❌ Using no-store Everywhere

Many developers disable caching while debugging and forget to remove it.

This forces every request to hit your server and database.


❌ Forgetting Cache Invalidation

Updating the database doesn't automatically refresh cached pages.

Use:

  • revalidateTag()

  • revalidatePath()

  • updateTag()

when content changes.


❌ Very Short Revalidation Times

Example:

revalidate: 5

Refreshing every five seconds often defeats the purpose of caching.

Choose a TTL based on how frequently your data actually changes.


❌ Caching User-Specific Data

Never cache content that depends on:

  • Cookies

  • Authentication

  • User preferences

Each user should receive their own response.


Performance Tips

βœ” 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.


Quick Cheat Sheet

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

Final Thoughts

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! πŸš€


Useful Resources

πŸ“š 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

https://swr.vercel.app

πŸ“š TanStack Query

https://tanstack.com/query/latest


Suggested SEO Titles

  • 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


Meta Description

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.


Suggested Tags

nextjs react typescript performance webdev caching cloudflare vercel prisma softwareengineering


Cover Image Prompt

"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."