Opting Out of Caching
Next.js caches aggressively by default, at three different layers: the Data Cache (individual fetch() results), the Full Route Cache (rendered output of static routes), and the client-side Router Cache (prefetched/visited segments in the browser). Most of the time this is exactly what you want. But some routes genuinely need fresh data on every single request — a dashboard, a stock ticker, an admin panel — and Next.js gives you a specific opt-out for each layer.
Opting out at the Data Cache layer
By default, fetch() requests inside a Server Component are cached indefinitely. Pass cache: 'no-store' to skip the Data Cache for that specific request:
async function getLiveStats() {
const res = await fetch('https://api.example.com/stats', {
cache: 'no-store',
})
return res.json()
}
export default async function Dashboard() {
const stats = await getLiveStats()
return <StatsPanel data={stats} />
}This is the most surgical option — it only affects the one fetch() call, leaving every other request on the page free to be cached normally.
Opting out at the route level
Sometimes an entire route needs to be dynamically rendered on every request — for example if it reads cookies() to render per-user content, or you simply don't want Next.js to attempt static optimization. The route segment config does this:
// app/dashboard/page.tsx
export const dynamic = 'force-dynamic'
export default async function DashboardPage() {
// Every fetch() here is re-run on every request,
// and the route is excluded from the Full Route Cache.
const data = await getLiveStats()
return <StatsPanel data={data} />
}Opting out on the client
The Router Cache lives in the browser and is unaffected by anything you set on the server. To force the current view to discard its cached segments and refetch, call router.refresh() from a Client Component:
'use client'
import { useRouter } from 'next/navigation'
export default function RefreshButton() {
const router = useRouter()
return <button onClick={() => router.refresh()}>Refresh data</button>
}Which technique addresses which layer
Technique | Layer it affects | Scope |
|---|---|---|
fetch(url, { cache: 'no-store' }) | Data Cache | A single fetch() call |
export const dynamic = 'force-dynamic' | Full Route Cache + Data Cache | The entire route |
router.refresh() | Client-Side Router Cache | The current view, client-side only |
A middle ground: time-based revalidation
Use no-store / force-dynamic for data that must never be stale, even for a second.
Use time-based revalidate for data that can tolerate being a little behind.
Use on-demand revalidatePath() / revalidateTag() when a mutation should immediately invalidate specific cached content.