On this page

Cache Lifetimes

Server cache entries share a common model for freshness, tags, invalidation, and storage. These policies are shared by Page Caching, RSC Caching, and Caching in Server Components.

Lifetimes

A server cache entry is fresh until revalidate, stale until expire, and unusable after expire:

SettingBehavior
revalidateThe entry is fresh until this time passes. Afterward, it is stale but can still be served and refreshed.
expireThe entry is usable until this time passes. Afterward, it must be rendered again.

Without expire, an entry stays usable for a year, so revalidate alone keeps serving the entry while it refreshes in the background. Set expire only when a stale entry must stop being served.

When several lifetime settings contribute to one render, the shortest value wins. expire must be at least as long as revalidate, a shorter value is clamped to revalidate. Lifetimes are capped at one year.

Cache settings
Page render
page + layout
Lifetime
revalidate=300sexpire=3600s
Tags
products
getProducts
"use cache"
Lifetime
revalidate=60sexpire=900s
Tags
productsinventory
getInventory
durableCache()
Lifetime
revalidate=120sexpire=600s
Tags
inventory
How settings combine
Shortest lifetime wins
revalidate and expire use their minimum
Tags are combined
all tags are kept
Page + RSC entries
Lifetime
revalidate=60sexpire=600s
Tags
productsinventory

Each cache scope owns its own entry, but settings from a nested scope also contribute to the parent page and RSC entries. In this example, getProducts and getInventory keep separate entries, while their shortest revalidate and expire values constrain the parent entries and their tags are merged into them. Parent settings do not flow down into nested entries.

Tags

A tag is a label shared by entries that should be invalidated together. When a tag is invalidated, every matching entry is treated as expired on its next read and refreshed as needed.

Tags from nested scopes are merged into parent entries, so invalidating a tag of a nested scope will also invalidate the parent scopes.

Server tags belong to server entries, client segment tags are separate and are managed by the client router. See Segment Caching.

cacheLife

Use cacheLife to set the revalidate and optional expire values for the current server cache scope:

import { cacheLife } from "@rebask/core/cache"

export default async function ProductsPage() {
  cacheLife({ revalidate: 300, expire: 3600 })
  return <ProductList />
}

The scope of a cacheLife call determines which entries receive its settings. In a page or layout, the settings contribute to the page's HTML and RSC entries. Inside a "use cache" function, they apply to that function's entry and flow upward to the containing render's page and RSC entries.

Multiple cacheLife calls are aggregated using the most restrictive lifetime. The smallest revalidate and expire values are used. expire is kept at least as long as revalidate.

cacheTag

Use cacheTag to associate the current server cache scope with one or more tags:

import { cacheTag } from "@rebask/core/cache"

export async function getProducts() {
  cacheTag("products")
  return db.products.findAll()
}

export async function getProduct(id: string) {
  cacheTag("products", `product:${id}`)
  return db.products.find(id)
}

Use cacheTag in a page, layout, or any other Server Component to tag the page and RSC entries produced by that render. It can also be called inside a "use cache" function to tag that function's entry and contribute the tag to the containing page and RSC entries. Tags are accumulated on each scope's own entry and also merged into their parent entries.

revalidateTag

Use revalidateTag from server-side code to invalidate every server entry associated with a tag:

"use server"

import { revalidateTag } from "@rebask/core/cache"

export async function updateProducts() {
  await db.products.updateAll()
  await revalidateTag("products")
}

It can be called from Server Functions, route handlers, and Server Components. The client router's router.revalidateTag is a separate API that only revalidates client segments.

When a tag is revalidated, Rebask records the invalidation time. Entries carrying that tag from before the invalidation are treated as expired on their next read and produce a new value.