Composition (route × Suspense × ISR × fetch-cache)
Every cache layer stacking on one page. The outer timestamp ticks per request (force-dynamic). Three Suspense boundaries stream their real content as it settles; each wraps an <ISR> fragment with a different TTL. The slowest fragment additionally fetches with next.revalidate, so layered caching keeps even the outer-miss render cheap.
Tip
Cache layers don't cancel each other out: a 30s ISR miss can still find the fetch result in the FetchCache (TTL 8s), saving the upstream round-trip. Add a tag (tags: ["composition"]) to selectively invalidate the FetchCache via /__bext/sdk/cache/purge-tag without touching the ISR fragments.
Output (refresh repeatedly to watch the cadences)
Outer (live): 2026-06-15T17:50:49.339Z
- loading fast…
- loading medium…
- loading slow…
Cadence map
| Layer | Cadence | Cache |
|---|---|---|
| Outer | every request | — |
| Fast fragment | ~5s | FragmentCache (timed) |
| Medium fragment | ~15s | FragmentCache (timed) |
| Slow fragment | ~30s | FragmentCache (timed) |
| Slow's upstream fetch | ~8s (only on slow miss) | FetchCache + tag "composition" |
import { Suspense } from "@bext-stack/framework/streaming";
import { ISR } from "@bext-stack/framework/cache";
export const dynamic = "force-dynamic";
export default function Page() {
const outer = new Date().toISOString();
return (
<div>
<p>Outer (live): {outer}</p>
<Suspense fallback={<p>loading fast…</p>}>
<ISR cacheKey="composition:fast" ttl={5}>
{async () => {
await new Promise(r => setTimeout(r, 200));
return `<p>Fast (5s ISR): ${new Date().toISOString()}</p>`;
}}
</ISR>
</Suspense>
<Suspense fallback={<p>loading medium…</p>}>
<ISR cacheKey="composition:medium" ttl={15}>
{async () => {
await new Promise(r => setTimeout(r, 400));
return `<p>Medium (15s ISR): ${new Date().toISOString()}</p>`;
}}
</ISR>
</Suspense>
<Suspense fallback={<p>loading slow…</p>}>
<ISR cacheKey="composition:slow" ttl={30}>
{async () => {
await new Promise(r => setTimeout(r, 800));
// fetch with its own 8s next-cache layer.
const r = await fetch("https://demo.bext.dev/api/echo?key=composition", {
next: { revalidate: 8, tags: ["composition"] },
});
const data = await r.json();
return `<p>Slow (30s ISR + 8s fetch-cache): ${data.receivedAt}</p>`;
}}
</ISR>
</Suspense>
</div>
);
}