Fragment cache (ISR boundary)
The outer page is dynamic — every request re-renders. The
inner subtree is wrapped in <ISR ttl=10>
and is served from cache for 10 seconds. Refresh quickly: the
outer timestamp ticks, the inner one stays frozen until the
TTL expires and the next render re-stores it.
Output
Outer (live): 2026-05-03T10:35:48.848Z
How it works
The page renders normally; when the JSX runtime hits the
<ISR> boundary it calls a host bridge
__bextIsrFragmentLookup. On hit it returns the
cached HTML and the function child is skipped. On miss the
function runs, the result is stored via
__bextIsrFragmentStore, and the HTML is yielded
back into the parent stream.
Cache lives in
bext_core::cache::fragment::FragmentCache — the
same store the layout cache uses, with a separate TTL-aware
map. Storage is in-process today; disk-backing + cross-process
coherence is the next iteration.
Source
import { ISR } from "@bext-stack/framework/cache";
// The outer page is dynamic — re-renders on every request.
// The inner subtree is cached for 10s via <ISR ttl={10}>.
// Refresh quickly and the inner timestamp stays frozen; the outer
// timestamp ticks per request.
export default function Page() {
const outer = new Date().toISOString();
return (
<div>
<p>Outer (live): {outer}</p>
<ISR cacheKey="featured" ttl={10}>
{async () => {
// Simulate slow data — this only runs on cache miss.
await new Promise(r => setTimeout(r, 50));
const inner = new Date().toISOString();
return `<p style="background:#eef;padding:.5rem">Inner (cached 10s): ${inner}</p>`;
}}
</ISR>
</div>
);
}