The cachecaching & revalidation

Your page can be static and up-to-date at the same time.

That used to be a contradiction. use cache renders a component once and keeps the output as a cache entry that every visitor gets. That is the static half. A cache tag is the handle on that entry: pull it and the entry expires, for everyone, at once. That is the up-to-date half. Nothing renders a replacement until someone asks for the page, and everyone after them gets the fresh entry free.

The stamp below is this page’s own entry, wearing a six-character fingerprint so you can tell one render from the next. Revalidate it and a fresh one reaches every reader of this page in about the time it takes the label to change back. It feels like one event.

It is three, and slow motion shows you each of them. Expire the entry, then ask for the page, and watch the color: it changes twice, not once. The gap between the two is what your cache logs have been naming all along. The request that refills the entry logs REVALIDATED, reason tag-based deletion, because that request did the rendering. STALE is the same gap handled softly: the old entry served while a fresh one renders.

live

entry #b31d84

rendered
11 Oct 2026, 02:03:50 UTC
served
from the static shell — the same entry every visitor gets

expires this page’s cache entry and renders a fresh one — for every visitor, immediately.

bake.ts + actions.ts + the ask
// bake.ts — the cache entry (lives in the static shell)
export async function getBake() {
  "use cache: remote"; // plain "use cache" is per-instance memory on a serverless host;
                       // remote is one entry, and every page that reads it agrees
  cacheTag("landing-shell");
  cacheLife("days");
  return {
    bakedAt: new Date().toISOString(),
    id: crypto.randomUUID().slice(0, 6), // the fingerprint — and a CSS color
  };
}

// actions.ts — the mutation
"use server";

export async function rebakeShell() {
  updateTag("landing-shell"); // expires it now, for everyone
  // The tag's expiry is stamped after this render finishes, so the bake in
  // this response was cached for nobody. The next request makes the real one.
  return { rebakedAt: new Date().toISOString() };
}

// rebake-panel.tsx — button two, the "ask"
router.refresh(); // no cache API anywhere — one more request for the page,
                  // indistinguishable from a new tab or another visitor