use cache - A new era of caching in Next.js 15
Next.js course ยท Module 9: Integrations and Advanced Features
In the server rooms of Metropolis Quantum 2150, where every millisecond counts, you will learn about the revolutionary caching API introduced in Next.js 15. The use cache directive replaces the deprecated unstable_cache and brings a much more intuitive way to manage the cache.
What is the use cache directive?
use cache is a new directive (similar to use server or use client) that marks functions or entire files as cacheable. Next.js automatically manages the cache lifecycle and revalidation.
Enabling dynamicIO
To use use cache, you first need to enable the experimental dynamicIO flag:
1// next.config.js
2module.exports = {
3 experimental: {
4 dynamicIO: true,
5 },
6};Basic use cache usage
Caching a single function
1// app/lib/data.ts
2async function getProducts() {
3 'use cache';
4
5 const response = await fetch('https://api.quantum-store.com/products');
6 return response.json();
7}Caching an entire file
1// app/lib/cached-data.ts
2'use cache';
3
4export async function getUsers() {
5 const response = await fetch('https://api.quantum-store.com/users');
6 return response.json();
7}
8
9export async function getOrders() {
10 const response = await fetch('https://api.quantum-store.com/orders');
11 return response.json();
12}cacheLife - Controlling cache lifetime
The cacheLife function lets you precisely define how long data should be stored in the cache.
Predefined profiles
Next.js ships with built-in time profiles:
1import { cacheLife } from 'next/cache';
2
3async function getNews() {
4 'use cache';
5 cacheLife('minutes'); // cache for a few minutes
6
7 return fetch('https://api.quantum-news.com/latest').then(r => r.json());
8}
9
10async function getStaticContent() {
11 'use cache';
12 cacheLife('days'); // cache for days
13
14 return fetch('https://api.quantum-cms.com/static').then(r => r.json());
15}
16
17async function getRealTimeData() {
18 'use cache';
19 cacheLife('seconds'); // cache for seconds
20
21 return fetch('https://api.quantum-sensors.com/data').then(r => r.json());
22}Available built-in profiles:
| Profile | stale | revalidate | expire | Description |
|--------|-------|------------|--------|------|
| seconds | - | 1s | 60s | For rapidly changing data |
| minutes | 5min | 1min | 1h | For data updated every few minutes |
| hours | 5min | 1h | 1d | For hourly data |
| days | 5min | 1d | 1w | For rarely changing data |
| weeks | 5min | 1w | 30d | For near-static data |
| max | 5min | 30d | 1y | Maximum cache duration |
Custom cacheLife configurations
You can define your own profiles in next.config.js:
1// next.config.js
2module.exports = {
3 experimental: {
4 dynamicIO: true,
5 cacheLife: {
6 quantumFast: {
7 stale: 10, // seconds - time when data is "fresh"
8 revalidate: 30, // seconds - time after which to revalidate in background
9 expire: 300, // seconds - maximum lifetime
10 },
11 quantumProducts: {
12 stale: 60,
13 revalidate: 300,
14 expire: 3600,
15 },
16 },
17 },
18};Using a custom profile:
1import { cacheLife } from 'next/cache';
2
3async function getQuantumProducts() {
4 'use cache';
5 cacheLife('quantumProducts');
6
7 return fetch('https://api.quantum-store.com/products').then(r => r.json());
8}Inline configuration
You can also define the configuration directly:
1import { cacheLife } from 'next/cache';
2
3async function getCriticalData() {
4 'use cache';
5 cacheLife({
6 stale: 5,
7 revalidate: 15,
8 expire: 60,
9 });
10
11 return fetch('https://api.quantum-critical.com/data').then(r => r.json());
12}cacheTag - Tagging and selective revalidation
cacheTag lets you label the cache with tags that can later be used for selective revalidation.
Basic tagging
1import { cacheTag, cacheLife } from 'next/cache';
2
3async function getProduct(id: string) {
4 'use cache';
5 cacheTag(`product-${id}`);
6 cacheLife('hours');
7
8 return fetch(`https://api.quantum-store.com/products/${id}`).then(r => r.json());
9}
10
11async function getCategory(slug: string) {
12 'use cache';
13 cacheTag('categories', `category-${slug}`);
14 cacheLife('days');
15
16 return fetch(`https://api.quantum-store.com/categories/${slug}`).then(r => r.json());
17}Revalidating by tags
1// app/actions/revalidate.ts
2'use server';
3
4import { revalidateTag } from 'next/cache';
5
6export async function updateProduct(id: string, data: ProductData) {
7 // Update product in the database
8 await db.products.update(id, data);
9
10 // Revalidate cache for this product
11 revalidateTag(`product-${id}`);
12}
13
14export async function refreshAllCategories() {
15 // Revalidate all categories at once
16 revalidateTag('categories');
17}Practical example - Quantum Store Dashboard
1// app/lib/dashboard-data.ts
2import { cacheTag, cacheLife } from 'next/cache';
3
4// Product data - cache for hours, tagged
5export async function getDashboardProducts() {
6 'use cache';
7 cacheTag('dashboard', 'products');
8 cacheLife('hours');
9
10 const response = await fetch('https://api.quantum-store.com/dashboard/products');
11 return response.json();
12}
13
14// Sales stats - cache for minutes (frequent updates)
15export async function getSalesStats() {
16 'use cache';
17 cacheTag('dashboard', 'sales');
18 cacheLife('minutes');
19
20 const response = await fetch('https://api.quantum-store.com/dashboard/sales');
21 return response.json();
22}
23
24// User data - cache for days
25export async function getUserStats() {
26 'use cache';
27 cacheTag('dashboard', 'users');
28 cacheLife('days');
29
30 const response = await fetch('https://api.quantum-store.com/dashboard/users');
31 return response.json();
32}1// app/actions/dashboard-actions.ts
2'use server';
3
4import { revalidateTag } from 'next/cache';
5
6export async function refreshDashboard() {
7 // Revalidate the entire dashboard
8 revalidateTag('dashboard');
9}
10
11export async function refreshSalesOnly() {
12 // Revalidate only sales
13 revalidateTag('sales');
14}1// app/dashboard/page.tsx
2import { getDashboardProducts, getSalesStats, getUserStats } from '@/lib/dashboard-data';
3import { refreshDashboard, refreshSalesOnly } from '@/actions/dashboard-actions';
4
5export default async function DashboardPage() {
6 const [products, sales, users] = await Promise.all([
7 getDashboardProducts(),
8 getSalesStats(),
9 getUserStats(),
10 ]);
11
12 return (
13 <div className="quantum-dashboard">
14 <header className="flex justify-between items-center">
15 <h1>Dashboard Quantum Store</h1>
16 <div className="flex gap-2">
17 <form action={refreshSalesOnly}>
18 <button type="submit">Refresh sales</button>
19 </form>
20 <form action={refreshDashboard}>
21 <button type="submit">Refresh all</button>
22 </form>
23 </div>
24 </header>
25
26 <div className="grid grid-cols-3 gap-6">
27 <ProductsWidget data={products} />
28 <SalesWidget data={sales} />
29 <UsersWidget data={users} />
30 </div>
31 </div>
32 );
33}Differences between use cache and unstable_cache
| Feature | use cache | unstable_cache | |-------|-----------|----------------| | Syntax | Directive | Wrapper function | | Time configuration | cacheLife() | revalidate option | | Tagging | cacheTag() | tags option | | Scope | Function or file | Function only | | Status | Stable (Next.js 15) | Deprecated |
Migrating from unstable_cache
Before (old way):
1import { unstable_cache } from 'next/cache';
2
3const getCachedProducts = unstable_cache(
4 async () => {
5 return fetch('https://api.com/products').then(r => r.json());
6 },
7 ['products'],
8 { revalidate: 3600, tags: ['products'] }
9);After (new way):
1import { cacheTag, cacheLife } from 'next/cache';
2
3async function getProducts() {
4 'use cache';
5 cacheTag('products');
6 cacheLife('hours');
7
8 return fetch('https://api.com/products').then(r => r.json());
9}Summary
The use cache directive in Next.js 15 introduces:
- Simpler syntax - declarative caching with a directive
- Flexible time control -
cacheLifewith profiles or custom configurations - Selective revalidation -
cacheTagfor precise refresh - Better performance - optimized cache management
In Metropolis Quantum 2150, where data flows at the speed of light, proper caching is the difference between a lightning-fast application and one that barely keeps up. Master use cache and your Next.js applications will run at top speed!
Code for this lesson: App.tsx
1import React, { useState } from 'react';
2
3// use cache - New era of caching in Next.js 15
4// Demonstration of 'use cache' directive and its configuration
5
6export default function UseCacheDemo() {
7 const [cacheStrategy, setCacheStrategy] = useState('default');
8 const [data, setData] = useState<any>(null);
9 const [isFetching, setIsFetching] = useState(false);
10
11 const strategies = [
12 { id: 'default', label: 'Default Cache', ttl: '30s', description: 'Default caching with revalidation every 30s' },
13 { id: 'force-cache', label: 'Force Cache', ttl: 'infinite', description: 'Data is cached permanently' },
14 { id: 'no-store', label: 'No Store', ttl: '0', description: 'No caching - always fresh data' },
15 { id: 'revalidate', label: 'Revalidate', ttl: '60s', description: 'ISR - revalidation every 60 seconds' },
16 ];
17
18 // TODO: Implement fetchWithCache - simulate different caching strategies
19 // TODO: Show differences in response times for different strategies
20
21 return (
22 <div style={{ background: '#0f0f23', minHeight: '100vh', padding: 20, color: '#e0e0e0', fontFamily: 'monospace' }}>
23 <h1 style={{ color: '#64ffda' }}>use cache - Next.js 15</h1>
24 {/* TODO: Caching strategy selector */}
25 {/* TODO: "Fetch data" button */}
26 {/* TODO: Display response time and data */}
27 {/* TODO: Strategy comparison */}
28 </div>
29 );
30}