QasimCode.
ServicesPortfolioBlogContactHire Me
Home/Blog/Next.js use cache Returning Stale Data After Deploy
Next.jsReactCaching

Next.js use cache Returning Stale Data After Deploy

Next.js 16 use cache keeps serving old data after a production deploy. Here is why it happens and the exact revalidateTag fix with working code.

MQ

Muhammad Qasim

Senior Full Stack Developer

April 17, 2026
5 min read

Problem

Shipped a Next.js 16.2 site to Vercel last Tuesday. New pricing page, new product copy, everything looked right locally. Opened production and half the components were still showing last week's data. Ran a hard refresh. Nothing. Purged the browser cache. Nothing. Redeployed. Nothing.

If you've been using the new "use cache" directive and seeing this, you're not alone. I searched r/nextjs yesterday and there were four threads in the last 48 hours with the same symptom:

"My use cache data is frozen in production. Works fine in dev. Redeploying doesn't refresh it."

Here's what's happening and how I fixed it.

Why It Happens

"use cache" is the new cache primitive Next.js introduced as a replacement for ad-hoc fetch() caching and unstable_cache. When you mark a function with it, Next.js stores the result with a cache key derived from the arguments, and serves that result on subsequent calls.

// app/lib/pricing.ts
"use cache";

export async function getPricing(plan: string) {
  const res = await fetch(`https://api.qasimcode.com/pricing/${plan}`);
  return res.json();
}

That's nice. Here's the catch: by default the cache entry persists across deploys because the cache key doesn't include the build ID. Vercel's Data Cache is persistent between deployments on purpose — that's the whole point, so cold-starts don't hammer your database.

But that means if your data source changes (you updated the JSON, edited the CMS, bumped a price), the cached response stays. Users see the old value until one of three things happens:

  1. The cacheLife() window expires
  2. You call revalidateTag() on a tag attached to the cache entry
  3. You call revalidatePath() on the affected route

If you didn't attach a cacheTag() and you didn't set a sensible cacheLife(), your cache entry defaults to cacheLife('default') — which is roughly 15 minutes stale + 1 year revalidate. You get stuck with stale data until the end of time.

The Fix

Three pieces to get this working cleanly. I use this pattern on every client project now.

1. Attach a tag to every cached function.

// app/lib/pricing.ts
import { cacheTag, cacheLife } from "next/cache";

export async function getPricing(plan: string) {
  "use cache";
  cacheTag(`pricing:${plan}`);
  cacheLife("hours");

  const res = await fetch(`https://api.qasimcode.com/pricing/${plan}`);
  if (!res.ok) throw new Error("Pricing fetch failed");
  return res.json();
}

Two things to notice. First, "use cache" goes inside the function when you need cacheTag() and cacheLife() calls alongside it — the function-level directive gives you access to those helpers. Second, cacheLife("hours") is a named profile; you can define custom profiles in next.config.ts or use the built-ins (seconds, minutes, hours, days, weeks, max).

2. Revalidate on the write path.

Anywhere you mutate the source of truth — admin action, webhook, cron — call revalidateTag():

// app/api/pricing/update/route.ts
import { revalidateTag } from "next/cache";

export async function POST(req: Request) {
  const { plan, price } = await req.json();
  await savePricingToDB(plan, price);
  revalidateTag(`pricing:${plan}`);
  return Response.json({ ok: true });
}

Now the next request after the update skips the cache, refetches, and stores a fresh entry under the same tag. No deploy needed.

3. Add a deploy-time invalidation for safety.

This is the one I missed on the client site that burned me. Even with tags, I want a backstop: whenever I ship a new build, nuke the caches that are tied to build-time content (copy, marketing pages, static pricing fallbacks). Add a post-deploy hook in your Vercel project:

# In Vercel → Project → Settings → Deploy Hooks
# Or via a GitHub Action after successful deploy:
curl -X POST "https://qasimcode.com/api/revalidate" \
  -H "Authorization: Bearer $REVALIDATE_TOKEN"

And the route:

// app/api/revalidate/route.ts
import { revalidateTag } from "next/cache";
import { headers } from "next/headers";

export async function POST() {
  const auth = (await headers()).get("authorization");
  if (auth !== `Bearer ${process.env.REVALIDATE_TOKEN}`) {
    return new Response("Unauthorized", { status: 401 });
  }

  ["pricing:starter", "pricing:pro", "pricing:enterprise", "marketing"].forEach(
    (tag) => revalidateTag(tag)
  );

  return Response.json({ revalidated: true });
}

That got my client's pricing page showing fresh numbers within seconds of the next deploy. No manual purges, no redeploys.

Gotchas I Hit

Dev mode lies. next dev doesn't persist the cache the same way production does. You can ship code that works perfectly locally and fails the moment it hits Vercel's Data Cache. Always test a preview deploy before assuming your caching is right.

cacheLife overrides nested calls. If function A is cacheLife("days") and calls function B with cacheLife("minutes"), the outer profile wins for the combined entry. Double-check with the Next.js debug logs (NEXT_PRIVATE_DEBUG_CACHE=1).

Tags don't cascade. revalidateTag("pricing") won't invalidate pricing:starter. Tag names are literal strings. If you want hierarchy, either add multiple tags per entry or normalize your tag names.

The official Next.js caching docs explain the mental model, but they're light on the "this is the actual pattern for production" angle. If you're comparing stacks before committing, my Next.js vs WordPress guide covers where each one shines.

Stuck on a Next.js Caching Bug?

I debug Next.js 16 cache and revalidation issues on production Vercel deployments. If your app is serving stale data and you can't figure out why, send me the repo on my services page and I'll dig in.

Need Help With This?

I offer professional web development services — WordPress, React/Next.js, performance optimization, and technical SEO.

Get in Touch
All Posts

About the Author

MQ

Muhammad Qasim

Senior Full Stack Developer with 5+ years experience in React, Next.js, and WordPress. Based in Pakistan, working globally.

Need a Web Developer?

I build WordPress sites, React apps, and optimize web performance.

View Services

Related Posts

  • GA4 Double-Counting Page Views in Next.js App Router5 min read
  • GA4 Page Views Not Tracking in Next.js App Router5 min read
  • Next.js 16 revalidateTag Not Working: Production Fix5 min read

QasimCode.

Senior Full Stack Developer building web solutions that deliver measurable growth.

hello@qasimcode.com

Services

  • WordPress Dev
  • React / Next.js
  • Performance
  • E-commerce
  • Technical SEO

Resources

  • Blog
  • Portfolio

Company

  • About
  • Contact
  • LinkedIn

© 2026 Muhammad Qasim. All rights reserved.

Pakistan — Remote worldwide