/**
* Next.js 16 - Cache Components with "use cache" Directive
*
* NEW in Next.js 16: Explicit opt-in caching with "use cache" directive.
* Replaces implicit caching from Next.js 15.
*
* This template shows component-level, function-level, and page-level caching.
*/
// ============================================================================
// Example 1: Component-Level Caching
// ============================================================================
'use cache'
// This entire component will be cached
export async function CachedProductList() {
const products = await fetch('https://api.example.com/products')
.then(r => r.json())
return (
Products
{products.map((product: { id: string; name: string; price: number }) => (
-
{product.name} - ${product.price}
))}
)
}
// ============================================================================
// Example 2: Function-Level Caching
// ============================================================================
// File: lib/data.ts
'use cache'
export async function getExpensiveData(id: string) {
console.log('Fetching expensive data...') // Only logs on cache miss
// Simulate expensive operation
await new Promise(resolve => setTimeout(resolve, 1000))
const data = await fetch(`https://api.example.com/items/${id}`)
.then(r => r.json())
return data
}
// Usage in component (not cached itself):
import { getExpensiveData } from '@/lib/data'
export async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const product = await getExpensiveData(id) // Cached by function
return (
{product.name}
{product.description}
)
}
// ============================================================================
// Example 3: Page-Level Caching
// ============================================================================
// File: app/blog/[slug]/page.tsx
'use cache'
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts')
.then(r => r.json())
return posts.map((post: { slug: string }) => ({
slug: post.slug,
}))
}
export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`)
.then(r => r.json())
return (
{post.title}
)
}
// ============================================================================
// Example 4: Partial Prerendering (PPR) - Mix Static & Dynamic
// ============================================================================
// File: app/dashboard/page.tsx
// Static component (cached)
'use cache'
async function StaticHeader() {
return (
)
}
// Dynamic component (NOT cached) - separate file without "use cache"
// File: components/dynamic-user-info.tsx
import { cookies } from 'next/headers'
export async function DynamicUserInfo() {
const cookieStore = await cookies()
const userId = cookieStore.get('userId')?.value
if (!userId) {
return Please log in
}
const user = await fetch(`https://api.example.com/users/${userId}`)
.then(r => r.json())
return (
Welcome, {user.name}
Balance: ${user.balance}
)
}
// Page combines static + dynamic (Partial Prerendering)
import { DynamicUserInfo } from '@/components/dynamic-user-info'
export default function DashboardPage() {
return (
{/* Cached (static) */}
{/* Not cached (dynamic) */}
)
}
// ============================================================================
// Example 5: Selective Caching with Multiple Functions
// ============================================================================
// Cache expensive operations, skip cheap ones
// Cached function
'use cache'
export async function getPopularPosts() {
const posts = await fetch('https://api.example.com/posts/popular')
.then(r => r.json())
return posts
}
// NOT cached (changes frequently)
export async function getRealtimeMetrics() {
const metrics = await fetch('https://api.example.com/metrics/realtime')
.then(r => r.json())
return metrics
}
// Component uses both
export async function Dashboard() {
const popularPosts = await getPopularPosts() // Cached
const metrics = await getRealtimeMetrics() // NOT cached
return (
Popular Posts
{popularPosts.map((post: { id: string; title: string }) => (
- {post.title}
))}
Realtime Metrics
Active users: {metrics.activeUsers}
Requests/min: {metrics.requestsPerMinute}
)
}
// ============================================================================
// Example 6: Cache with Revalidation (using tags)
// ============================================================================
// File: app/actions.ts
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
await fetch('https://api.example.com/posts', {
method: 'POST',
body: JSON.stringify({ title, content }),
})
// Revalidate cached posts
revalidateTag('posts', 'max')
}
// File: lib/posts.ts
'use cache'
export async function getPosts() {
const response = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }, // Tag for revalidation
})
return response.json()
}
// ============================================================================
// Example 7: Conditional Caching (Cache Based on User Role)
// ============================================================================
import { cookies } from 'next/headers'
export async function getContent() {
const cookieStore = await cookies()
const userRole = cookieStore.get('role')?.value
if (userRole === 'admin') {
// Don't cache admin content (changes frequently)
return fetch('https://api.example.com/admin/content').then(r => r.json())
}
// Cache public content
return getCachedPublicContent()
}
'use cache'
async function getCachedPublicContent() {
return fetch('https://api.example.com/public/content').then(r => r.json())
}
// ============================================================================
// Example 8: Inline "use cache" (Granular Control)
// ============================================================================
export async function MixedCachingComponent() {
// This function call is cached
const cachedData = await (async function() {
'use cache'
return fetch('https://api.example.com/slow-data').then(r => r.json())
})()
// This function call is NOT cached
const freshData = await fetch('https://api.example.com/fresh-data').then(r => r.json())
return (
Cached: {cachedData.value}
Fresh: {freshData.value}
)
}
// ============================================================================
// Migration Guide: Next.js 15 → Next.js 16
// ============================================================================
// ❌ BEFORE (Next.js 15 - Implicit Caching):
/*
// All Server Components were cached by default
export async function MyComponent() {
const data = await fetch('https://api.example.com/data')
return {data.value}
}
// To opt-out of caching:
export const revalidate = 0 // or export const dynamic = 'force-dynamic'
*/
// ✅ AFTER (Next.js 16 - Explicit Opt-In Caching):
/*
// Components are NOT cached by default
export async function MyComponent() {
const data = await fetch('https://api.example.com/data')
return {data.value}
}
// To opt-IN to caching, add "use cache"
'use cache'
export async function MyCachedComponent() {
const data = await fetch('https://api.example.com/data')
return {data.value}
}
*/
// ============================================================================
// Cache Behavior Summary
// ============================================================================
/**
* "use cache" can be added to:
* 1. ✅ Components (entire component cached)
* 2. ✅ Functions (function output cached)
* 3. ✅ Pages (entire page cached)
* 4. ✅ Layouts (layout cached)
* 5. ✅ Inline async functions (granular caching)
*
* Default behavior (without "use cache"):
* - Server Components: NOT cached (change from Next.js 15)
* - fetch() calls: Cached by default (unchanged)
*
* Revalidation:
* - Use revalidateTag() to invalidate cache by tag
* - Use updateTag() for immediate read-your-writes
* - Use refresh() for uncached data only
*
* When to use "use cache":
* ✅ Expensive computations (database queries, API calls)
* ✅ Stable data (product catalogs, blog posts)
* ✅ Partial Prerendering (static header + dynamic user info)
*
* When NOT to use "use cache":
* ❌ Real-time data (metrics, notifications)
* ❌ User-specific data (unless using cookies/headers for cache keys)
* ❌ Frequently changing data (stock prices, live scores)
*/