SyntaxStudy
Sign Up
Next.js Caching, Revalidation, and Streaming
Next.js Beginner 1 min read

Caching, Revalidation, and Streaming

Next.js has a multi-layered caching strategy. The Request Memoization layer deduplicates identical fetch calls within a single server render. The Data Cache layer (persistent across requests) stores fetch responses on the server and can be controlled via the cache and next.revalidate options. The Full Route Cache stores rendered HTML and RSC payloads for statically generated routes. The Router Cache stores RSC payloads in the browser for client-side navigation. On-demand revalidation lets you purge the cache programmatically without waiting for a revalidation interval. The revalidatePath(path) function from next/cache invalidates all cached data for a given path and any segment below it. The revalidateTag(tag) function invalidates all cache entries associated with a specific tag. You assign tags to fetch calls using the next.tags option. React Suspense boundaries combined with Server Components enable streaming. By wrapping a slow-loading component in a Suspense boundary and placing that slow component in a separate async Server Component, Next.js streams the fast parts of the page immediately and streams the slow parts when they are ready. This is far better than waiting for all data before sending any HTML.
Example
// On-demand revalidation in a Route Handler
// app/api/revalidate/route.ts

import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const secret = request.headers.get('x-revalidate-secret');
  if (secret !== process.env.REVALIDATE_SECRET) {
    return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
  }

  const { path, tag } = await request.json();

  if (tag) {
    revalidateTag(tag); // purge all entries with this tag
  } else if (path) {
    revalidatePath(path); // purge a specific path
  }

  return NextResponse.json({ revalidated: true });
}

// Tagging fetch calls for targeted invalidation
// app/products/page.tsx
async function getProducts() {
  const res = await fetch('https://api.example.com/products', {
    next: {
      tags: ['products'], // tag this cache entry
      revalidate: 3600,
    },
  });
  return res.json();
}

// Streaming with Suspense
// app/page.tsx
import { Suspense } from 'react';
import SlowWidget from './SlowWidget';

export default function Home() {
  return (
    <main>
      <h1>Dashboard</h1>
      <Suspense fallback={<p>Loading widget...</p>}>
        <SlowWidget />
      </Suspense>
    </main>
  );
}