Nested ISR (TTL coupling)

Page ISR (5s TTL) wrapping an <ISR ttl=30s> fragment. The outer rerolls every 5 seconds; the inner cache survives across multiple outer cycles because its TTL is longer than the outer's. Refresh repeatedly: the outer timestamp ticks every ~5s, the inner stays frozen for ~30s.

Output

Outer (page ISR ttl=5s): 2026-05-03T10:35:42.299Z

Inner (fragment ISR ttl=30s): 2026-05-03T10:35:42.400Z

Reading the cadences

  • 0s: cold — both timestamps captured fresh.
  • 0–4s: every refresh hits the page cache; both timestamps frozen.
  • 5s: page cache expires → re-render. The outer timestamp updates. The inner ISR is consulted: age = 5s, TTL = 30s → HIT; the cached HTML from t=0 is reused. Outer page is re-stored.
  • 10s, 15s, 20s, 25s: same pattern — outer ticks, inner stays at t=0.
  • 30s: outer expired again, inner consulted at age=30s vs TTL=30s → EXPIRED → re-render with a fresh timestamp.

The inner cache pays for itself: between t=5s and t=29s, the slow function child runs zero times even though the outer page rerolls 5 times.

Anti-pattern: if you flipped the TTLs (outer=30s, inner=5s), the inner would expire long before the outer rerolls. Every outer-miss would also be an inner- miss, so the inner cache would never hit — dead weight. The interop matrix in plan/granular-isr-streaming/ flags this as the case to avoid.

Source

import { ISR } from "@bext-stack/framework/cache";

// Page-level ISR: 5 second TTL.
export const revalidate = 5;

export default function Page() {
    const outer = new Date().toISOString();
    return (
        <div>
            <p>Outer (page ISR ttl=5s): {outer}</p>
            <ISR cacheKey="nested:inner" ttl={30}>
                {async () => {
                    // Slow render — the value of the ISR cache. Skipped on
                    // page-cache hit; consulted on page-cache miss; only
                    // re-runs when its own 30s TTL expires.
                    await new Promise(r => setTimeout(r, 100));
                    return `<p>Inner (fragment ISR ttl=30s): ${new Date().toISOString()}</p>`;
                }}
            </ISR>
        </div>
    );
}