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.

Output (refresh repeatedly to watch the cadences)

Outer (live): 2026-05-05T09:18:19.830Z

  • loading fast…
  • loading medium…
  • loading slow…

Cadence map

LayerCadenceCache
Outerevery request
Fast fragment~5sFragmentCache (timed)
Medium fragment~15sFragmentCache (timed)
Slow fragment~30sFragmentCache (timed)
Slow's upstream fetch~8s (only on slow miss)FetchCache + tag "composition"

Source

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("http://127.0.0.1:3032/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>
        );
    }