💡
Warning: "use cache" is an experimental feature in the Next.js Canary release channel and may change in the future. Use it with caution.

In this article, we build on previous articles, where we manipulated data with actions and fetched data with server 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.