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

Inner (cached 10s): 2026-05-03T10:35:48.898Z

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>
    );
}