BuildBot

Foundations

The Next.js 16 caching model

Lesson 2 of 7

What you'll learn

  • Understand why Next.js 16 made caching explicit
  • Use use cache to cache expensive, shareable work
  • Know what should never be cached (per-user data)

Earlier versions of Next.js cached aggressively and implicitly — which led to a lot of "why is my data stale?" confusion. Next.js 16 flips this: nothing is cached unless you ask for it. All dynamic code runs at request time by default.

Opting into the cache

You mark a function or component as cacheable with the use cache directive, and tune its lifetime with cacheLife and cacheTag:

import { unstable_cacheLife as cacheLife } from "next/cache";

async function getCourseCatalog() {
  "use cache";
  cacheLife("hours");
  return db.courses.findMany();
}

Cache things that are shared across users and change rarely: a marketing page, a published course catalog, lesson content. Keep things per-user and live — progress, entitlements, the signed-in user — dynamic.

Never cache identity

A signed-in user's progress or permissions must render fresh every request. Cache the lesson content (same for everyone); never cache whether this user finished it.

Invalidation

When content changes, you invalidate by tag rather than waiting for a timer:

import { revalidateTag } from "next/cache";
// after a publish webhook fires:
revalidateTag("course-catalog");

The exercise below is a tiny model of cache-vs-fresh. The getCached function pretends to be expensive; run it twice and watch the cache hit on the second call.

Cache hit vs. miss

Run it. The first call 'computes'; the second is served from the cache. Try clearing the cache and running again.

Loading editor…

Next up: real data. We'll design a Convex schema and learn why indexes are mandatory.