In this article, we build on previous articles, where we manipulated data with actions and fetched data with server components:
- Instant feedback using the useOptimistic hook in React
- Passing server data to client components in React
- Handling data mutations in client components
Now, we’ll introduce caching with the experimental "use cache" directive in Next.js, which can be applied across routes, specific components, and functions.
In Next.js 15, caching is disabled by default, meaning that data isn’t cached between requests or navigations without explicit instructions. This default setup grants developers full control, enabling them to tailor caching solutions to their applications' unique needs.
Enable dynamicIO
To enable dynamicIO (and support for the directive), configure your next.config.ts file as shown below:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
experimental: {
dynamicIO: true,
},
};
export default nextConfig;
Setting Up Caching
We have a Drizzle setup with a comments schema defined elsewhere. The focus of this article is on two key operations: fetching comments and inserting comments. We'll be applying caching to improve performance and minimise redundant database calls.
Method 1: Fetching Comments and Caching
Instead of fetching comments from the database on every page load, we can cache the comments using the "use cache" directive to reduce DB calls.
getComments.ts
"use server";
import { db } from "@/db";
import { comments } from "@/schema";
import { unstable_cacheLife, unstable_cacheTag } from "next/cache";
export async function getComments() {
"use cache";
unstable_cacheLife("days");
unstable_cacheTag("comments");
const result = await db.select().from(comments).all();
return result;
}
unstable_cacheLife
sets the cache with a specific profile (e.g., “days”). Profiles such as “hours,” “max,” and “weeks” are also available, custom profiles can be created in next.config.ts. The documentation recommends using profiles, though you can set cache parameters like stale, revalidate, and expire manually if needed.
unstable_cacheTag
tags the cache entry with a unique identifier (very similar to the indentifier in tanstack-query
). It helps with:
- Tracking and managing the cache: This tag is used to reference the cached comments data so you can easily manage it.
- Cache invalidation: When the comments cache becomes outdated (e.g., when a new comment is added), you can invalidate the cache using this tag to ensure fresh data is fetched the next time comments are requested.
Method 2: Inserting Comments and Invalidating Cache
In this method, after inserting the comment into the database, we use revalidateTag
to invalidate the relevant cache. This ensures that the next time comments are fetched, the cache will be cleared, and fresh data will be retrieved.
createComment.ts
"use server";
import { db } from "@/db";
import { comments } from "@/schema";
import { revalidatePath, revalidateTag } from "next/cache";
export async function createComment(formData) {
const message = formData.get("message");
const insertedComment = await db.insert(comments).values({ message });
// Invalidate cache
revalidateTag("comments");
return insertedComment;
}
Conclusion
By leveraging Next.js’s "use cache"
directive, we have fine-grained control over how frequently accessed data like comments is fetched. This flexibility improves performance and user experience by reducing unnecessary refetching while keeping data up-to-date through cache invalidation.
Member discussion