On this page

Page Caching

Pages in Rebask are cached by default in production. A page cache entry can contain either a Complete HTML representation of the page or a static HTML shell with dynamic holes that are resumed at runtime.

Page cache layers

Rebask builds reusable shells for dynamic routes. A shell contains static HTML and postponed state that tells React how to resume the dynamic parts for a request at runtime. See What a resume stores to learn more.

EntryParams baked into the entryWhenPurpose
Shared shellNoneBuild timeFallback for any path that matches a dynamic route
Intermediate shellParams declared by a layout's prerenderParamsBuild timeA page fallback for a known parameter combination
Pathname shellAll params in the URL pathnameBuild time or background promotionResumable output for a path with all route parameters known
Complete HTMLAll params in the URL pathnameBuild time or background promotionComplete HTML for a cached path that can be served without a resume

If a shared or intermediate shell contains no dynamic holes, there is nothing to resume. The same shell serves every matching pathname, so navigating between them only appends the route tree and does not fetch a new segment.

At build time, Rebask uses the route params known for the render and whether rendering reads request I/O. Together, they determine whether it generates a shared shell, an intermediate shell, a Pathname shell, or Complete HTML:

Build time
Dynamic page + empty params
Shared shell
No params are baked in
Dynamic page + layout params
Intermediate shell
Known layout params are baked in
Static page or full params + request I/O
Pathname shell
Request content resumes later
Static page or full params + no request I/O
Complete HTML
Static HTML, no resume

A Pathname shell and Complete HTML are two representations stored under the same pathname cache key. When rendering completes without accessing request I/O, Rebask stores the result as Complete HTML, which can be served directly. When rendering accesses request I/O, the result is a resumable Pathname shell instead.

Runtime request
GET /products/42
Lookup order
pathname key1
Complete HTML
intermediate key2
Intermediate shell
shared key3
Shared shell
First match
Complete HTML
Serve directly
Any shell
Serve + resume
Background promotion
After response
Render pathname again
No request I/O
Complete HTML
Request I/O
Pathname shell

Shared shell

For dynamic pages that allow unknown parameter values, Rebask prerenders the page at build time with no route parameters to produce a shared shell.

Requests
/products/1GET
/products/2GET
/products/3GET
Shared shell
/products/[id]PPR HIT
Product ID resumes here
Resume per request
id=1
Product 1
id=2
Product 2
id=3
Product 3

This shell can serve any allowed path that matches the page because no parameters are baked into it. Rebask uses it when no more specific page cache entry is available.

The shared shell is keyed by the route file, not by a concrete pathname. The shell is served first, then Rebask resumes the postponed parts of the page for the requested path.

Intermediate shells

prerenderParams on layouts can declare known parameter combinations. Rebask renders an intermediate shell for each combination and bakes those known parameters into the static HTML:

app/
  [locale]/
    layout.tsx                 → declares locale values: "en", "es"
    products/[id]/page.tsx     → /:locale/products/:id

Shared shell:       /[locale]/products/[id]
Intermediate shell: /en/products/[id]
Pathname entry:     /en/products/42
// app/[locale]/layout.tsx — intermediate shells
export function prerenderParams() {
  return { params: [{ locale: "en" }, { locale: "es" }] }
}

// app/[locale]/products/[id]/page.tsx — complete parameter combinations
export function prerenderParams() {
  return {
    params: [
      { locale: "en", id: "1" },
      { locale: "en", id: "2" },
      { locale: "es", id: "1" },
    ],
  }
}

For /en/products/42, an intermediate shell can contain locale: "en" while leaving 42 as a dynamic hole.

At runtime, when multiple intermediate shells match a request, Rebask selects the most specific one and resumes any remaining dynamic parameters:

Request
/en/electronics/42
id=42
Known shells
locale=encategory=electronics
richest match
locale=en
shallower match
Selected shell
locale=encategory=electronics
ID 42 resumes here

The combinations returned by layout prerenderParams become the available intermediate shell variants. Rebask records them in the build manifest and uses them to select the most specific shell for each request, while platform adapters use them to package prerender routes.

Generating new variants at runtime would make shell keys unpredictable and could lead to an unbounded number of intermediate entries, so unknown combinations use the shared shell instead.

Pathname shells

Rebask creates a Pathname shell when all route parameters for a page are known and rendering reads request I/O. At build time, this includes pages without dynamic segments and dynamic pages whose page-level prerenderParams lists complete parameter combinations.

At runtime, after a less specific shell serves a request, Rebask can render that pathname in the background and store the result under its pathname cache key. When request I/O leaves postponed state, the result is a Pathname shell.

Request
GET /products/42
Less specific shell
Serve shared or intermediate shell
Resume
Fill postponed content
Response finished
HTML delivered
Resume complete
Background promotion
Render pathname again
Pathname shell
ph:<deployment-id>:/products/42
Postponed state

Rebask does not promote pages that accessed request I/O to Complete HTML, because doing so could cache data from an individual request (cache poisoning). When a resume is necessary a Pathname shell is created.

Platform adapters can perform this promotion by sending a new upgrade request to Rebask after the shell has resumed. On Vercel for example, the adapter sends a request in the background and the platform stores the result in its prerender cache. If an upgrade render accesses request I/O inside a <Suspense> boundary, Rebask returns a resumable Pathname shell instead of Complete HTML.

Complete HTML

This entry is the complete HTML representation for a concrete pathname. It has no postponed state, so Rebask can serve it directly without a resume.

Request
GET /products/42
Complete HTML
ph:<deployment-id>:/products/42
Product 42
Complete page content
Response
Served directly. No resume required.

A Complete HTML hit is equivalent to serving a static HTML file for that pathname. The complete document has already been generated, so the request only needs to read and return it.

How a cache hit is chosen

For a normal HTML request, Rebask performs this lookup for a dynamic page:

  1. In parallel, look up the pathname entry, such as ph:<deployment-id>:/products/42, and the best matching shell entry from the build manifest.
  2. If they both missed and the best matching shell was an intermediate shell, look up the shared shell next.
  3. If all lookups miss, render the request.

A shell entry is a PPR hit: Rebask serves its HTML and resumes its dynamic parts, or only appends the route tree of the path for a shell that rendered complete. Complete HTML is served directly.

A fresh entry means a cache HIT. An entry past its revalidate window is STALE but still usable so Rebask serves it and schedules a background refresh. An entry past its expire window or invalidated by a tag is treated as a MISS, so Rebask continues to the next shell or renders the request.

Page cache keys

EntryLogical key
Pathname shell or Complete HTMLph:<deployment-id>:<pathname>
Shared shell HTMLph:<deployment-id>:<route-file>
Intermediate shell HTMLph:<deployment-id>:<route-file>:<encoded-shell-params>
Default not-found HTMLph:<deployment-id>:__default_not_found__

Intermediate shells can use encoded params such as locale=en|category=books.

Page cache keys are prefixed with a deploymentId, generated for each production build. You can learn more about why in Server Cache Keys.