ISR cache

bext caches the rendered HTML per [[route_rules]] entry. Refresh quickly and you see the same timestamp; wait past ttl_ms and the next request re-renders. The render counter below is bumped server-side on every cache MISS — it lives in the cache layer's L1/L2 store, so it survives V8 isolate churn. Response carries x-bext-cache: hit | stale | miss and x-bext-render-count: N.

Tip
With <code>swr_ms</code> set, visitors always get an instant response (stale HTML) even while a fresh render runs in the background. Without it, the first visitor after TTL expiry blocks waiting for a full render. Aim for <code>swr_ms ≥ ttl_ms / 2</code> on non-critical pages.

Output

rendered at 2026-06-15T17:58:02.700Z

render # 1

Refresh rapidly — the timestamp stays the same. After 60 s the next refresh triggers a fresh render and the counter bumps.

src/app/examples/isr/page.tsxTSX
// bext.config.toml
[[route_rules]]
pattern = "/**"
render = "isr"
ttl_ms = 60000   // cache for 60 s
swr_ms = 30000   // serve stale for another 30 s while revalidating

// src/app/examples/isr/page.tsx — every render stamps the current time.
// Under ISR the HTML is cached for ttl_ms; refresh inside the window
// returns the same HTML (same timestamp). 1 is
// substituted server-side with a per-route counter that bumps on every
// cache MISS — it tells you how many fresh renders this route has done
// over its lifetime, regardless of V8 isolate churn.

export default function Page() {
  const stamp = new Date().toISOString();
  return (
    <p>
      rendered at {stamp} — render #
      <Raw html="1" />
    </p>
  );
}