Next.js use cache, cacheLife and cacheTag: How One View Counter Rebuilt Every Page
Vercel's usage page told me this blog had used 2 h 32 min of function CPU in 30 days: 80% of everything on my account. It's a blog. The posts only change when I deploy. The cause was one line near the top of my pages: const stats = await getPostStats ( ) ; // read + comment counts, cached for 5 minutes The counts were cached for 5 minutes. But the 5-minute timer didn't stay on the counts. Every…
A developer found that a blog's use of cached view counts was causing all pages to refresh every 5 minutes, even though the data only changed when new posts were added. The issue stemmed from a line in the code that read `const stats = await getPostStats(); // read + comment counts, cached for 5 minutes`. This line caused the cached data to expire every 5 minutes, leading to stale content on various pages.
To address this, the developer likened the server cache to a shared fridge, where each visitor shares the same set of results. They introduced the `use cache` directive, which stores the result of a function or component and reuses it for subsequent requests. The `cacheLife` parameter determines how long the cached data remains fresh before being refreshed in the background. The developer used `cacheLife(max)` to set the cache expiration to the maximum duration.
To fine-tune the caching behavior, the developer introduced the `cacheTag` parameter, which allows them to invalidate specific cached entries when the underlying data changes. They created a `cacheTag` for comments and used it with the `revalidateTag` function to invalidate the cache when new comments were added. Additionally, they used `updateTag` in a server action to refresh the cache immediately when a comment is approved.
The developer also pointed out that TanStack Query, a separate caching library for client-side data, should be used in conjunction with server cache. While server cache is suitable for shared data that doesn't change frequently, TanStack Query offers more granular control over per-user data, such as carts and dashboards. They recommend using server cache for data that changes only when new content is deployed, and TanStack Query for live or per-user data that requires real-time updates.
To implement this approach, the developer placed the cached data on the server and fetched live or per-user data on the client-side using a route handler. They connected the `useQuery` hook from TanStack Query to the server's cached data and configured it with a `staleTime` parameter that matched the server's refresh time. When the user modifies data, the developer clears both the server cache and the TanStack Query cache by using the `updateTag` function in the server action and `queryClient.invalidateQueries` in the browser.
By following these steps, the developer was able to drastically improve the performance of their blog. Instead of refreshing every 5 minutes, the pages now updated only when new posts were added, reducing the build output time from every 5 minutes to every 1 day. This optimization ensured that the blog remained fast and efficient while still providing up-to-date content for users.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.