Caching in Next.js 16: "use cache", cacheLife and cacheTag
Caching in Next.js used to be implicit and confusing. Recent versions moved to an explicit model: data is fetched fresh by default, and you opt in to caching with the 'use cache' directive.
Turn on Cache Components
// next.config.ts
import type { NextConfig } from 'next';
const config: NextConfig = { cacheComponents: true };
export default config;
Cache a function
import { cacheLife, cacheTag } from 'next/cache';
export async function getProducts(category: string) {
'use cache';
cacheLife('hours'); // built-in profile: how long to keep it
cacheTag('products'); // label for targeted invalidation
return db.product.findMany({ where: { category } });
}
Arguments become part of the cache key, so getProducts('books') and getProducts('games') are cached separately.
Cache a component or a whole page
export default async function PricingTable() {
'use cache';
cacheLife('days');
const plans = await getPlans();
return <Table plans={plans} />;
}
Mixing cached and dynamic content
Request-specific data (cookies, headers, search params) can't be cached. Wrap the components that use it in <Suspense>; Next.js serves the cached shell instantly and streams the dynamic parts.
export default function Page() {
return (
<>
<PricingTable /> {/* cached */}
<Suspense fallback={<p>Loading your plan...</p>}>
<CurrentUserPlan /> {/* reads cookies, rendered per request */}
</Suspense>
</>
);
}
Invalidation
revalidateTag('products', 'max')- mark tagged entries stale; the next visitor gets the old data while fresh data is fetched in the background.updateTag('products')- inside a Server Action, expire immediately so the user sees their own change.revalidatePath('/products')- refresh everything for a route.
Tip: decide caching per piece of data, not per page. Product lists can be cached for hours; stock levels maybe not at all.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.