Course
Data Cache
Next.js Mastery: From Fundamentals to Full-Stack
Unlock the power of Next.js with this comprehensive course! Starting with the basics, you’ll learn essential skills such as routing, data fetching, and styling. Progress through practical projects, including building your own React Notes app, to gain hands-on experience. Dive into the source code to understand the inner workings and core principles of Next.js. Perfect for developers with a basic knowledge of React and Node.js, this course ensures you’ll be ready to create high-performance full-stack applications efficiently. Join us and master Next.js, backed by industry experts and a supportive learning community.
Data Cache
1. How It Works
Next.js has its own data caching solution that can store data across server requests and build deployments. This is achieved by extending the fetch API, allowing each request to have its own caching strategy.
Unlike React's request memoization, which only applies during component tree rendering and doesn't need to handle cache updates, Next.js's data caching is more persistent and requires cache invalidation strategies.
By default, fetch requests in Next.js will be cached persistently and won't automatically reset. You can configure the caching behavior using fetch options cache and next.revalidate:
fetch('https://...', { cache: 'force-cache' | 'no-store' })fetch('https://...', { next: { revalidate: 3600 } })
This is the working principle of Next.js data caching:
Let's explain: When the request is made for the first time during rendering, both request memoization and data caching will miss, triggering the request. The returned result is stored in both the request memoization and data cache.
On subsequent calls, because the
{ cache: 'no-store' }
parameter was added, the request memoization will miss due to different parameters. This parameter causes data cache skipping, resulting in another request. Since no-store is configured, the data cache won't cache the result, but request memoization will cache it as usual.2. Duration
Data cache persists across incoming requests and deployments unless revalidated or opted out.
3. Revalidation
Next.js offers two ways to update the cache:
- Time-based revalidation: Revalidate data after a certain period and when new requests are made. Suitable for data that doesn't change frequently and where freshness isn't critical.
- On-demand revalidation: Manually revalidate data based on events. This can be done using path-based or tag-based methods, suitable for scenarios requiring the latest data as soon as possible.
Time-based Revalidation
To use time-based revalidation, set the revalidation time (in seconds) using the next.revalidate option in the fetch request:
// Revalidate every hourfetch('https://...', { next: { revalidate: 3600 } })
Or configure it via a route segment configuration to apply to all fetch requests in that segment:
// layout.jsx / page.jsx / route.jsexport const revalidate = 3600
Here's a diagram illustrating time-based revalidation:
This shows that the request will not automatically update after 60 seconds. Instead, it will revalidate when a new request is made after 60 seconds. The first request after 60 seconds will still return the cached value, but Next.js will update the cache with new data. The second request after 60 seconds will use the new data.
On-demand Revalidation
For on-demand revalidation, data can be updated based on path (revalidatePath) or cache tags (revalidateTag).
revalidatePath is used in route handlers or Server Actions to manually clear cache data for specific paths:
javascriptCopy coderevalidatePath('/')
revalidateTag relies on Next.js's cache tag system. Declare a tag when making a fetch request, then revalidate requests with a specific tag in route handlers or Server Actions:
javascriptCopy code// Using tagsfetch('https://...', { next: { tags: ['a', 'b', 'c'] } })// Revalidating requests with a specific tagrevalidateTag('a')
Here's a diagram illustrating on-demand revalidation:
The first call to the request caches data as usual. When on-demand revalidation is triggered, the corresponding cache entries are removed from the cache. The next request will be treated as the first call, caching data again.
4. Opting Out
To opt out of data caching, you can use two methods:
- Set the cache option of the fetch request to no-store, ensuring data is re-fetched each time:javascriptCopy code
fetch('https://...', { cache: 'no-store' })
- Use a route segment configuration to affect all data requests in that segment:javascriptCopy code
export const dynamic = 'force-dynamic'
Practical Experience
Modify
app/page.js
with the following code:async function getData() { // The API returns a random cat image each time const res = await fetch('https://api.thecatapi.com/v1/images/search') return res.json()}
export async function generateMetadata() { const data = await getData() return { title: data[0].id }}
export default async function Page() { const data = await getData() return ( <> <h1>Image ID: {data[0].id}</h1> <img src={data[0].url} width="300" /> <CatDetail /> </> )}
async function CatDetail() { const data = await getData() return ( <> <h1>Image ID: {data[0].id}</h1> <img src={data[0].url} width="300" /> </> )}
The logic is straightforward. When you visit
/
, the generateMetadata
function, the page, and the child component will each call the API three times, returning a random cat image each time. In the production version, will the data returned by the three calls be consistent?Let's run it:
Whether you refresh normally or hard refresh, the images remain the same, and the data returned by the three API calls is consistent.
The reason is simple: the page is statically rendered during the build, and although the API is called three times, request memoization and data caching ensure the data returned is consistent.
Now let's disable data caching by adding code to
app/page.js
:// Force fetch to not cacheexport const fetchCache = 'force-no-store'
Run the production version again:
Conclusion
Let's compare request memoization and data caching:
- Request Memoization: React's data caching solution, lasting during component tree rendering to avoid multiple requests for the same data during rendering, improving performance.
- Data Cache: Next.js's data caching solution, persisting across deployments and requests. The cache doesn't expire unless revalidated or explicitly opted out, aiming to optimize application performance.
In real-world development, request memoization and data caching often coexist and work together.
References
- Building Your Application: Caching | Next.js