AvatarJuan VictorAboutStackProjectsBlogsInspiring Insights
Download CV
Home
About
Stack
Projects
Blogs
Inspiring Insights
Download CV
AvatarJuan Victor
AboutStackProjectsBlogsInspiring Insights

© 2025 JV Portfolio. All rights reserved.

Updated: November 18, 2024

Caching Semantics in Next.js 15: What’s Changed? (Chapter 2)

🚀 Introduction

Next.js 15 introduces a significant change in how caching works, moving towards an explicit opt-in model. Fetch requests, GET Route Handlers, and client navigations are no longer cached by default, ensuring more predictable and dynamic data fetching.

In this article, we’ll dive deep into:

  • What changed in caching behavior
  • How to explicitly enable caching
  • When and why to use different caching strategies
  • Performance implications of this update

Let's break it down. 🔍


⚡ What Changed in Next.js 15 Caching?

Previously, Next.js automatically cached fetch requests, API routes, and client-side navigations unless explicitly set to no-store . This could lead to stale data issues, where users received outdated responses unexpectedly.

Now, in Next.js 15:

  1. fetch requests default to no-store (previously force-cache).
  2. GET Route Handlers are no longer cached automatically.
  3. Client-side navigations always fetch fresh data unless caching is manually configured.

This shift gives developers more control over caching behavior, reducing unexpected results. However, it also requires explicit configuration to optimize performance.


🔧 How to Enable Caching Explicitly

Since caching is no longer automatic, developers must opt-in using force-cache, next.revalidate, or HTTP headers.

1. Enabling Fetch Caching

By default, fetch requests always retrieve fresh data unless explicitly cached.

✅ Example: Caching Fetch Requests

Alternatively, you can cache responses for a set period using next.revalidate:


2. Caching GET Route Handlers (app/api/route.ts)

In earlier versions, GET API routes automatically cached responses. Now, to enable caching, you need to use explicit headers.

✅ Example: Caching a GET API Route


3. Enabling Client-Side Page Caching

If a page fetches data dynamically and you want to cache responses, use next.revalidate.

✅ Example: Opting Into Page Caching


📉 Performance Implications

Since caching is opt-in, applications that rely on frequent API calls may experience increased request volume. Here’s how this change affects performance:

🔴 Potential Drawbacks

  • Higher API request volume since fresh data is always fetched unless explicitly cached.
  • Slower client-side transitions due to missing automatic caching.
  • Unexpected behavior in legacy applications that relied on implicit caching.

✅ When This Change is Beneficial

  • Ensures data consistency by always fetching fresh content.
  • Prevents stale data issues, especially in dynamic applications.
  • Gives developers control over caching, reducing unexpected side effects.

🚀 Best Practices

  • Use next.revalidate for periodic caching instead of full persistence.
  • Cache only non-critical or rarely changing data.
  • Apply Cache-Control headers in API responses where needed.

📌 Conclusion

Next.js 15’s explicit caching model improves data consistency but requires developers to opt into caching manually. While this may increase API load, it ensures predictable behavior across dynamic applications.

By using strategies like force-cache, next.revalidate, and explicit cache headers, you can optimize performance while maintaining fresh data.

For further insights, check out:

  • 📖 Next.js 15 Blog Post
  • 🎥 YouTube: Next.js 15 Caching Changes Explained

Would you like me to add a performance benchmark comparison for different caching strategies? 🚀

1
async function getProducts() {
2
const res = await fetch("https://api.example.com/products", {
3
cache: "force-cache", // Opt into caching
4
});
5
return res.json();
6
}
7
1
import { NextResponse } from "next/server";
2
3
export async function GET() {
4
const data = await fetch("https://api.example.com/data", {
5
cache: "force-cache", // Opt into caching
6
});
7
8
return NextResponse.json(await data.json(), {
9
headers: {
10
"Cache-Control": "public, max-age=300", // Cache for 5 minutes
11
},
12
});
13
}
14
1
export async function getServerSideProps() {
2
const data = await fetch("https://api.example.com/data", {
3
next: { revalidate: 120 }, // Cache for 2 minutes
4
});
5
6
return {
7
props: { data: await data.json() },
8
};
9
}
10
1
async function getProducts() {
2
const res = await fetch("https://api.example.com/products", {
3
next: { revalidate: 60 }, // Cache response but refresh every 60 seconds
4
});
5
return res.json();
6
}
7