Time-based Revalidation

ISR in the App Router

Set revalidate on a fetch or for the whole route. Next.js serves the cached version; after the time passes, the next request still gets the old page, while a fresh one is built in the background (stale-while-revalidate).

// app/deals/page.tsx — rebuild at most once every 10 minutes
export const revalidate = 600;

export default async function DealsPage() {
  const deals = await fetch('https://api.shopease.in/deals').then((r) => r.json());
  return <pre>{JSON.stringify(deals, null, 2)}</pre>;
}

Stale-While-Revalidate in TypeScript

Example

This version behaves like Next.js: a stale request returns the old value and triggers a background refresh.

type Entry<T> = { value: T; storedAt: number };

class SwrCache<T> {
  private store = new Map<string, Entry<T>>();
  constructor(private revalidate: number, private load: (key: string, now: number) => T) {}

  get(key: string, now: number): string {
    const hit = this.store.get(key);
    if (!hit) {
      const value = this.load(key, now);
      this.store.set(key, { value, storedAt: now });
      return 'MISS  -> ' + value;
    }
    const age = now - hit.storedAt;
    if (age < this.revalidate) return 'HIT   -> ' + hit.value;
    // serve stale, refresh "in the background"
    this.store.set(key, { value: this.load(key, now), storedAt: now });
    return 'STALE -> ' + hit.value + '  (refreshed for next time)';
  }
}

let version = 0;
const cache = new SwrCache<string>(60, (key, now) => key + ' v' + ++version + ' @' + now + 's');

for (const t of [0, 30, 61, 62, 130]) {
  console.log('t=' + String(t).padStart(3) + '  ' + cache.get('/deals', t));
}

Output

t=  0  MISS  -> /deals v1 @0s
t= 30  HIT   -> /deals v1 @0s
t= 61  STALE -> /deals v1 @0s  (refreshed for next time)
t= 62  HIT   -> /deals v2 @61s
t=130  STALE -> /deals v2 @61s  (refreshed for next time)

Choosing the Number

DataTypical revalidate
Blog post, About pageNone (static) or 1 day
Product description1 hour + on-demand when edited
Price, stock on listing pages1–5 minutes + on-demand
Cart, order statusDo not cache (dynamic)