2025-05-23 Web Development
Caching in Next.js 15 – A Developer's Guide
By O. Wolfson
Introduction: Understanding Caching in Next.js 15
Next.js 15 introduces a modern approach to caching built around server components, static rendering, and fine-grained control over dynamic behavior. Whether you're building a high-performance landing page, a real-time dashboard, or a hybrid app with both static and dynamic data, Next.js gives you powerful tools to manage what gets cached, when, and for how long.
This tutorial provides a hands-on walkthrough of the different caching strategies available in Next.js 15 using the App Router. Each example demonstrates a core concept in isolation so you can see how caching affects rendering behavior, performance, and data freshness.
Use the links below to explore:
- Fully static rendering
- Incremental Static Regeneration (ISR)
- Fully dynamic, uncached pages
- API response caching with
fetch()
Whether you're new to caching or looking to deepen your understanding of how Next.js handles it under the hood, this guide will give you the foundation to make smart, performant decisions in your own projects.
1. Getting Started with a Fresh App
Install a new Next.js 15 project:
Make sure you select the App Router during setup.
2. Understand Caching Modes
Next.js 15 supports three main cache modes for server-rendered pages and data:
Setting | Behavior | Use Case |
---|---|---|
dynamic = 'force-static' | Fully static | Blog pages, landing pages |
revalidate = 60 | ISR (cache for 60s) | News, product pages |
dynamic = 'force-dynamic' | No cache | Dashboards, auth pages |
3. Demo: Static Page with dynamic = 'force-static'
In app/static-example/page.tsx
:
Now visit /static-example
— the page is built once and cached permanently until you redeploy.
4. Demo: Revalidated Page with revalidate = 10
In app/revalidated-example/page.tsx
:
Visit /revalidated-example
and refresh. You'll see the timestamp update every 10 seconds.
5. Demo: Fully Dynamic Page
In app/dynamic-example/page.tsx
:
Visit /dynamic-example
and see how it updates every time — no caching at all.
6. Demo: Caching API Data with fetch
In app/api-example/page.tsx
:
You can swap revalidate
with cache: 'no-store'
for real-time data.
7. Best Practices
🔹 Use revalidate
instead of dynamic = 'force-dynamic'
whenever possible
This allows for stale-while-revalidate, which is faster and more scalable.
🔹 Cache external API responses explicitly
Always use next: { revalidate }
or cache: 'no-store'
with fetch()
to control behavior.
🔹 Don’t over-cache dynamic data
Avoid caching user-specific data or authenticated content.
🔹 Group caching by layout if consistent
Example: a dashboard layout with dynamic = 'force-dynamic'
ensures all child pages inherit that behavior.
8. Standard Procedure for New Projects
When setting up caching:
- Default pages: use static or ISR (
revalidate: 60
) - Dynamic dashboards: use
dynamic = 'force-dynamic'
- API responses: use
fetch(..., { next: { revalidate } })
- Authenticated routes: disable caching entirely
Bonus: Manual Cache Revalidation
Set up an API route like /api/revalidate
:
This simulates an external CMS calling to clear cache.