NextjsClient-Side Router Cache

Client-Side Router Cache

Alongside the server-side Data Cache and Full Route Cache, the App Router keeps a third cache that lives entirely in the browser: the Client-Side Router Cache (sometimes just called the Router Cache). It is an in-memory store of the React Server Component payloads for route segments the user has already visited or that Next.js has prefetched on their behalf.

Because it lives in memory on the client, this cache is what makes back/forward navigation and prefetched links feel instant — there is no network round trip, no server render, just a swap of already-fetched React Server Component data into the page.

How it gets populated
  • Prefetching — a Link in the viewport is automatically prefetched, storing its RSC payload in the Router Cache before the user even clicks.

  • Visiting a route — navigating to any route stores its segments in the cache for the duration of the session.

TSX
import Link from 'next/link'

// Static routes are prefetched fully when the link enters the viewport.
// Dynamic routes prefetch just the shared layout, up to the nearest
// loading.tsx boundary, unless prefetch={true} is set explicitly.
export default function Nav() {
  return (
    <nav>
      <Link href="/docs">Docs</Link>
      <Link href="/pricing" prefetch={true}>
        Pricing
      </Link>
    </nav>
  )
}
Why back/forward feels instant

When the user navigates back to a route segment that is already sitting in the Router Cache, Next.js reuses that cached payload instead of asking the server for it again. Combined with React's handling of shared layouts, this is why moving between pages in an App Router site rarely shows a full-page loading spinner once a segment has been visited once.

Clearing the Router Cache
Note
The Router Cache is separate from the server-side caches, so revalidating data on the server with revalidatePath() or revalidateTag() does not, by itself, force the currently open browser tab to refetch anything.

To force the client to discard its cached segments and refetch from the server, call router.refresh() from a Client Component:

TSX
'use client'

import { useRouter } from 'next/navigation'

export default function RefreshButton() {
  const router = useRouter()

  return (
    <button
      onClick={() => {
        // Clears the Router Cache for the current view and re-fetches
        // fresh Server Component output from the server
        router.refresh()
      }}
    >
      Refresh
    </button>
  )
}

router.refresh() does not remount the page, reset scroll position, or lose client-side state such as useState — it simply asks the server for a fresh RSC payload for the current route and merges it in.

Router Cache vs. the other caches

Cache

Where it lives

Cleared by

Data Cache

Server

revalidateTag(), revalidatePath(), time-based revalidation

Full Route Cache

Server

Revalidation or a new deployment

Router Cache

Browser memory

router.refresh(), a full page reload, or the session ending

Warning
Server Actions that call revalidatePath() or revalidateTag() automatically invalidate the relevant Router Cache entries for the requesting client too — it is only manual server-side revalidation calls made outside of a request/response cycle (such as a webhook) that leave open tabs with stale cached segments until they refresh or navigate again.
Tip
If users report seeing stale data after an update but a hard refresh fixes it, the Router Cache is almost always the culprit — check whether your mutation path is actually triggering revalidation, or whether it just needs a router.refresh() call.