A router.push() stale data problem shows up when you navigate to a route a reader already visited: the page can render old content, even after the underlying data changed on the server. This happens because push performs a client-side navigation using Next.js's in-memory client cache whenever a match is available, rather than always asking the server for the latest data.
Reproducing the problem
Say a settings page updates a display name, then navigates back to a profile route the reader visited a moment earlier in the same session.
// app/settings/save-button.tsx
"use client";
import { useRouter } from "next/navigation";
export default function SaveButton({ onSave }: { onSave: () => Promise<void> }) {
const router = useRouter();
const handleSave = async () => {
await onSave();
router.push("/profile");
};
return <button onClick={handleSave}>Save</button>;
}If the profile route was already cached from an earlier visit, this navigation can render that cached version first, still showing the old display name, since a plain navigation has no built-in refetch step of its own.
Why this happens
The App Router keeps visited and prefetched routes in an in-memory client cache so that navigating back to them feels instant instead of triggering a new server request every time. That speed is the whole point of the cache, but it means a plain navigation can serve content that was correct when it was cached and is now out of date on the server.
The fix inside the current route
Call refresh right after the mutation to re-fetch the current route's data from the server and re-render its Server Components, without losing client-side state such as scroll position or an open menu.
"use client";
import { useRouter } from "next/navigation";
export default function SaveButton({ onSave }: { onSave: () => Promise<void> }) {
const router = useRouter();
const handleSave = async () => {
await onSave();
router.refresh();
router.push("/profile");
};
return <button onClick={handleSave}>Save</button>;
}This method always targets whichever route the reader is currently on. It does not accept a different route as an argument, so it cannot force a route the reader has not visited yet to update in advance of them getting there.
The fix for every reader, not just this one
When a mutation should invalidate a route's data everywhere, not only in the browser that made the change, use a server-side revalidation function inside the Server Action instead of relying on a client-side refresh.
revalidatePath and revalidateTag invalidate cached data from a Server Action or a Route Handler, and they work whether or not a project has Cache Components enabled. Inside a Server Action specifically, prefer updateTag for data cached with cacheTag under the use cache directive: it expires that tag immediately instead of serving stale content while a background refresh completes, so the reader who just made the change sees it right away.
For the full set of router methods this article builds on, see useRouter in the App Router: Push, Replace, Refresh, and Back. To see how a redirect after a Server Action differs from this client-side pattern, read useRouter vs redirect in Next.js: Client vs Server Navigation.
Rune AI
Key Insights
- push reuses cached route data when it is available instead of always fetching fresh data.
- refresh re-fetches the current route's data from the server without a full reload.
- refresh always targets the route you are currently on, not a route you pass in.
- revalidatePath and revalidateTag stay current in Next.js 16 and work with or without Cache Components enabled.
- updateTag only runs inside a Server Action and invalidates a cacheTag immediately instead of serving stale content first.
Frequently Asked Questions
Does a full browser refresh always show fresh data?
Does router.refresh accept a route as an argument?
Is revalidatePath the current way to invalidate data in Next.js 16?
Conclusion
push alone only performs a client-side navigation using whatever is already in the client cache, so a revisited route can show data that no longer matches the server. Call refresh to pull fresh data into the current route, or use a server-side revalidation function so every reader sees the update after a mutation.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.