In a Next.js App Router Client Component, what does `router.refresh()` from `useRouter` (next/navigation) actually do, and how does it differ from `window.location.reload()`?
answer
- one keeps the document alive
- server half re-runs, client half survives
- the URL never changes
- a new request is not always new data
- cache invalidation lives on the server
basics
~20 srouter.refresh() re-requests the current route from the server, re-renders its Server Components, and merges the new payload into the live page — keeping client state and scroll position. window.location.reload() throws the whole document away and starts over.
solid answer
~50 s`router.refresh()` asks the server for a fresh render of the route you are already on. Next drops the client Router Cache entry for that route, fetches a new RSC payload, and reconciles it into the mounted React tree — so Server Components re-run and show new data, while `useState` in client components, focus, and scroll position all survive. The URL does not change and no document is loaded. `window.location.reload()` is the blunt instrument: full document request, bundles re-parsed, hydration from scratch, every scrap of client state gone. The subtlety worth naming is that `refresh()` guarantees a new *request*, not new *data* — if the server still serves that route's data from a cache, you get the same bytes back, and the fix is to invalidate on the server rather than to refresh harder on the client.
code
tsx · 20 lines'use client';
import { useState, useTransition } from 'react';
import { useRouter } from 'next/navigation';
export function RefreshPanel() {
const router = useRouter();
const [isPending, startTransition] = useTransition();
const [note, setNote] = useState('');
return (
<div>
{/* this value survives the refresh */}
<input value={note} onChange={(e) => setNote(e.target.value)} />
<button onClick={() => startTransition(() => router.refresh())} disabled={isPending}>
{isPending ? 'Refreshing…' : 'Refresh server data'}
</button>
</div>
);
}go deeper
Know that router.refresh() comes from useRouter in next/navigation, needs a client component, and updates the page's server-rendered content without a full browser reload.
Explain the mechanics: it drops the client Router Cache entry for the route, refetches the RSC payload, and reconciles it into the live tree, which is why useState and scroll position survive.
Demonstrate the diagnosis: a refresh that returns stale content means the server served cached data. Show that you invalidate on the server and treat the client refresh as the consumer of that invalidation.
Own the freshness contract across the app: decide which surfaces are allowed to be stale and for how long, so teams stop reaching for refresh-on-interval as a substitute for a real data-freshness or push strategy.
## The shape of the problem A page rendered by Server Components got its data on the server. Something then changes that data — a mutation the user performed, a webhook, a background job. The browser is holding a React tree that was produced from stale server output. You need the server half of the page to run again without destroying the client half. That is exactly the job of `router.refresh()`. ## What refresh does, step by step ```tsx 'use client'; import { useRouter } from 'next/navigation'; export function RefreshButton() { const router = useRouter(); return <button onClick={() => router.refresh()}>Refresh</button>; } ``` Calling it: 1. **Invalidates the client Router Cache entry** for the current route, so the stale payload cannot be reused. 2. **Issues a new request to the server** for that route's RSC payload. The server re-runs the route's Server Components, including their data access. 3. **Merges the new payload into the existing React tree.** This is a reconciliation, not a remount: components that did not change keep their identity. Because it is a reconciliation inside a live document, the things that make a page feel "yours" survive: `useState` and `useReducer` values in client components that stay mounted, uncontrolled input values, the scroll position, focus, open dialogs, in-memory stores. The URL is untouched — refresh is not a navigation. ## What reload does instead `window.location.reload()` is a browser API, not a Next one. It performs a full document navigation to the current URL: new HTML, all JavaScript re-downloaded from cache or network and re-executed, React mounted from zero, hydration run again. Every piece of client state is gone, scroll goes back to where the browser decides, and the user watches a white flash. It is occasionally the right call — after a logout, after a session change that must invalidate everything in memory, after a deploy that changed the client bundle contract. As a data-freshness tool it is a sledgehammer. ## The trap: refresh does not invalidate server caches This is the part that separates a real answer from a memorized one. `router.refresh()` guarantees a fresh **request**. It does not guarantee fresh **data**. If the route's data access is served from a server-side cache, the re-render can produce exactly the same output, and the user sees the same stale values no matter how many times they hit refresh. The cure is on the server side: invalidate the cached entry there, then let the client pick up the new render. In a Server Action that just mutated something, the server-side invalidation and the refresh are two halves of one flow — invalidate what you changed on the server, and the client's fresh request then produces new output. So the diagnostic order when "my data is stale" comes in is: 1. Did a new request actually reach the server? (Network tab; a refresh is visible.) 2. Did the server re-run the data access, or serve it from cache? 3. Is the source of truth itself behind a cache (CDN, upstream API) that neither side controls? ## Refresh versus a navigation Three things get confused with each other: - **`router.refresh()`** — same route, fresh server render, client state kept. - **`router.push('/somewhere')`** — a different route, new history entry, the old page's segment unmounts. - **`redirect('/somewhere')`** from `next/navigation` — server-side control flow; it belongs in Server Components, Route Handlers and Server Actions, not in a click handler. A rule of thumb: if the user should stay where they are and just see newer content, refresh. If the user should end up somewhere else, navigate — and if the decision is being made on the server, let the server redirect rather than round-tripping a flag back to the client so it can push. ## Practical notes - `refresh()` returns nothing useful to await; the update lands when the payload arrives. If you need to show pending UI, drive it from a transition or your own loading state. - Calling it in a loop or on an interval turns your app into a polling client for a full route render. If you need live data at that cadence, that is a data-transport problem, not a routing one. - It re-runs the *server* half only. A client component's `useEffect` does not re-fire simply because a refresh happened, unless its dependencies changed.
- A user clicks your refresh button and still sees the old numbers. Where do you look?First confirm in the network tab that a request actually went out — if it did, the client did its job. Then check whether the server re-ran the data access or returned a cached result; `router.refresh()` forces a new request, not fresh data. If the server is caching that route's data, invalidate it there rather than refreshing again.
- Does router.refresh() re-run useEffect in the client components on the page?Not by itself. A refresh reconciles a new server payload into the mounted tree; client components that stay mounted keep their state, and an effect re-runs only if its dependency values changed as a result. If you need work to happen on every refresh, derive it from props that the server render actually changes.
- When would you deliberately choose window.location.reload() over router.refresh()?When keeping in-memory state would be wrong or unsafe: after a logout or a user switch, after a change to the auth session that stale stores might still reflect, or when the deployed client bundle is out of step with the server. In those cases discarding the JavaScript context is the point, not a side effect.
saying these in an interview costs you the question
- Says refresh reloads the page like F5
- Expects client component state to be reset by refresh
- Believes refresh bypasses every server-side cache
- Thinks refresh changes the URL or adds history
- Uses refresh on an interval as a live-data mechanism