Caching is a fundamental technique to optimize the performance of your Next.js applications by reducing redundant data fetches. By default, Next.js caches the results of fetch calls on the server, which can significantly speed up subsequent data requests.
In this guide, we’ll explore how to effectively utilize caching in Next.js, with practical examples to illustrate the concepts.
Understanding Next.js Data Cache
Next.js employs a persistent HTTP Data Cache to store the results of fetch requests. This cache can automatically scale and be shared across multiple regions depending on your deployment platform. By default, fetch calls in Next.js use the cache mode force-cache.
For Example:
fetch('https://jsonplaceholder.typicode.com/posts', { cache: 'force-cache' })
There are some scenarios where fetch requests won’t be cached:
- Inside a Server Action.
- In a Route Handler using the POST method.
Revalidating Cached Data
Revalidation is the process of invalidating the cached data and fetching the latest version. This ensures your application displays up-to-date information. Next.js supports two types of revalidation:
- Time-based Revalidation: Automatically revalidate data after a specified time interval.
- On-demand Revalidation: Manually revalidate data in response to specific events, such as form submissions.
Time-based Revalidation
To revalidate data at a specified interval, use the revalidate option in the fetch configuration:
fetch('https://jsonplaceholder.typicode.com/posts', { next: { revalidate: 3600 } })
Alternatively, you can set the revalidation frequency for an entire route segment:
export const revalidate = 3600;
On-demand Revalidation
For more granular control, Next.js allows revalidation of specific cache entries using tags. Here’s how to tag a fetch request and revalidate it on-demand:
//app/page.js
export default async function Page() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts', { next: { tags: ['posts'] } })
const data = await res.json()
console.log(data, " ::posts");
}
To revalidate this tagged fetch request:
//app/actions/revalidate.js
'use server'
import { revalidateTag } from 'next/cache'
export default async function revalidatePosts() {
revalidateTag('posts')
}
Handling Errors During Revalidation
If an error occurs during revalidation, Next.js will continue to serve the last successfully cached data and retry revalidation on the next request.
Opting Out of Data Caching
In cases where you need to disable caching, use the no-store option in the fetch configuration:
fetch('https://jsonplaceholder.typicode.com/posts', { cache: 'no-store' })
This fetch request will bypass the cache and fetch fresh data on every request.
Example: Fetching and Displaying Posts with Caching
Let’s put these concepts into practice by creating a simple Next.js page that fetches and displays posts from an API, with caching and revalidation.
//utils/getData.js
export const getData = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts', { next: { revalidate: 3600 } }) // revalidate every one hour
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}
//app/page.js
import { getData } from '@/utils/getData'
export default async function Home() {
const data = await getData()
return (
<main className="flex min-h-screen p-20 gap-20 flex-col ">
<div className="py-5 px-2 bg-slate-50 rounded-md flex items-center justify-center">
<h1 className="text-4xl text-green-900">Posts</h1>
</div>
{data.map((post) => (
<div key={post.id} className="flex py-5 px-2 bg-slate-50 rounded-md flex-col ">
<div className="flex flex-row gap-4 ">
<h2 className="text-black">Title: </h2><span className="text-black">{post.title}</span>
</div>
<div className="flex flex-row gap-4">
<p className="text-black">Body: </p><span className="text-black">{post.body}</span>
</div>
</div>
))}
</main>
);
}