Skip to content
Loadout

Fix

Next.js 16 revalidateTag and Cache Components

Answer

In Next.js 16, revalidateTag takes a cache profile. Pass the max profile for stale-while-revalidate. The single-argument call is deprecated. Inside a Server Action, use updateTag(tag) when the next request must see the user's own write.

  • Next.js 16.4.1
  • React 19.3
  • @supabase/ssr 0.12.7
  • @supabase/supabase-js current, installed alongside @supabase/ssr

Last verified 2026-10-15

Agents still call revalidateTag with only a tag, or they call router.refresh() from the client after a write. The Next.js + Supabase pack pins Next.js 16.4.1, React 19.3, @supabase/ssr 0.12.7, and @supabase/supabase-js (current, installed alongside @supabase/ssr). The pack tells you to call revalidatePath or revalidateTag after a write. That line leaves off the profile Next.js 16 requires. Async params are /fixes/nextjs-16-async-params-and-cookies. The interceptor filename is /fixes/nextjs-16-middleware-to-proxy.

revalidateTag(tag, profile) marks the tagged data stale. The next request starts a revalidation and is served stale content while it runs. Pass revalidateTag(tag, "max"). That profile is a one-year window. Another cacheLife profile, or an object with an expire number of seconds, sets a different window. revalidateTag(tag, { expire: 0 }) never serves stale content, so the next request blocks. Use that when the data must be gone immediately and you cannot call updateTag. revalidateTag(tag) with no profile is deprecated. It behaves like { expire: 0 }, still runs if TypeScript errors are suppressed, and that behavior may be removed. It can be called in Server Functions and Route Handlers. It cannot be called in a Client Component or in proxy.

updateTag(tag) is the read-your-own-writes call. It expires the tag immediately, and the next request waits for fresh data. updateTag can only be called from a Server Action. A Route Handler that calls it throws, so a webhook uses revalidateTag instead. revalidatePath still invalidates one page or layout path. revalidateTag invalidates every cached entry that carries that tag. A revalidation starts when a later request uses the tag, not when you call revalidateTag.

A tag does nothing until cached data carries it. Inside a "use cache" function or component, call cacheTag from next/cache. On a cached fetch, pass next.tags. Tags are case-sensitive and must not exceed 256 characters. A longer tag is never stored, so revalidating it does nothing. New create-next-app projects enable Cache Components. An existing app sets cacheComponents and partialPrefetching to true in next.config.ts. Do not cache a response that depends on a cookie or a user. Authenticated reads stay dynamic. Checked against https://nextjs.org/docs/app/api-reference/functions/revalidateTag and https://nextjs.org/docs/app/api-reference/functions/updateTag.

Last verified 2026-10-15. doc-verified; TypeScript snippets pass tsc --strict (TypeScript 5, @types/react 19) with zod and the Supabase client stubbed.

Pro

Next.js 16 + Supabase Pro rules, $1+

$1+ · pay what you want

RLS policy recipes, getClaims, Stripe webhooks, and Cache Components. Deeper than the free Next.js + Supabase pack.

Each pack ships as AGENTS.md, CLAUDE.md, Cursor .mdc, and Copilot instructions, with 12 months of free updates.

From the Next.js 16 + Supabase Pro rules

Enable RLS on every table in `public`. A table with no policy denies that command for `anon` and `authenticated`. A table with RLS off is readable and writable through the Data API. The free pack already says this. Write the policy, do not stop at `enable row level security`.
Name the role with `to authenticated`. Compare `(select auth.uid())` to `user_id`. The `select` wrapper is the form in the Supabase RLS guide. `auth.uid()` is null when the JWT is missing, and `null = user_id` is never true.
`select` and `delete` use `using`. `insert` uses `with check`, so a user cannot insert a row for someone else. `update` uses both: `using` picks the row, `with check` locks the row after the write, including `user_id`.
The secret key, and the legacy `service_role` key, bypass RLS. Use one only on the server, after you have checked the caller yourself. A browser client never gets that key. Supabase also says a secret key does not bypass RLS when the request still carries a user access token.
Next.js 16 + Supabase Pro rules, $1+

Next.js + Supabase pack — Next.js 16.4 and Supabase SSR, with the APIs that actually work.

doc-verified; TypeScript snippets pass tsc --strict (TypeScript 5, @types/react 19) with zod and the Supabase client stubbed

Sources in the pack

Docs

Questions

How do I call revalidateTag in Next.js 16?
In Next.js 16, revalidateTag takes a cache profile. Pass the max profile for stale-while-revalidate. The single-argument call is deprecated. Inside a Server Action, use updateTag(tag) when the next request must see the user's own write.
What is the difference between updateTag and revalidateTag?
updateTag expires the tag immediately and can only be called from a Server Action, so the next request waits for fresh data. revalidateTag(tag, "max") serves stale content while fresh data loads in the background, and it also runs in Route Handlers. The single-argument revalidateTag(tag) is deprecated.
How do I tag data for Cache Components?
Call cacheTag inside a "use cache" function or component, or pass next.tags on a cached fetch. Tags are case-sensitive and must not exceed 256 characters. Do not cache a response that depends on a cookie or a user.
When was this Next.js 16 revalidateTag rule last checked?
Last verified 2026-10-15. doc-verified; TypeScript snippets pass tsc --strict (TypeScript 5, @types/react 19) with zod and the Supabase client stubbed.