Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    f0rr0

    next-cache-components

    f0rr0/next-cache-components
    Coding
    45

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag

    SKILL.md

    Cache Components (Next.js 16+)

    Cache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route.

    Enable Cache Components

    // next.config.ts
    import type { NextConfig } from 'next'
    
    const nextConfig: NextConfig = {
      cacheComponents: true,
    }
    
    export default nextConfig
    

    This replaces the old experimental.ppr flag.


    Three Content Types

    With Cache Components enabled, content falls into three categories:

    1. Static (Auto-Prerendered)

    Synchronous code, imports, pure computations - prerendered at build time:

    export default function Page() {
      return (
        <header>
          <h1>Our Blog</h1>  {/* Static - instant */}
          <nav>...</nav>
        </header>
      )
    }
    

    2. Cached (use cache)

    Async data that doesn't need fresh fetches every request:

    async function BlogPosts() {
      'use cache'
      cacheLife('hours')
    
      const posts = await db.posts.findMany()
      return <PostList posts={posts} />
    }
    

    3. Dynamic (Suspense)

    Runtime data that must be fresh - wrap in Suspense:

    import { Suspense } from 'react'
    
    export default function Page() {
      return (
        <>
          <BlogPosts />  {/* Cached */}
    
          <Suspense fallback={<p>Loading...</p>}>
            <UserPreferences />  {/* Dynamic - streams in */}
          </Suspense>
        </>
      )
    }
    
    async function UserPreferences() {
      const theme = (await cookies()).get('theme')?.value
      return <p>Theme: {theme}</p>
    }
    

    use cache Directive

    File Level

    'use cache'
    
    export default async function Page() {
      // Entire page is cached
      const data = await fetchData()
      return <div>{data}</div>
    }
    

    Component Level

    export async function CachedComponent() {
      'use cache'
      const data = await fetchData()
      return <div>{data}</div>
    }
    

    Function Level

    export async function getData() {
      'use cache'
      return db.query('SELECT * FROM posts')
    }
    

    Cache Profiles

    Built-in Profiles

    'use cache'                    // Default: 5m stale, 15m revalidate
    
    'use cache: remote'           // Platform-provided cache (Redis, KV)
    
    'use cache: private'          // For compliance, allows runtime APIs
    

    cacheLife() - Custom Lifetime

    import { cacheLife } from 'next/cache'
    
    async function getData() {
      'use cache'
      cacheLife('hours')  // Built-in profile
      return fetch('/api/data')
    }
    

    Built-in profiles: 'default', 'minutes', 'hours', 'days', 'weeks', 'max'

    Inline Configuration

    async function getData() {
      'use cache'
      cacheLife({
        stale: 3600,      // 1 hour - serve stale while revalidating
        revalidate: 7200, // 2 hours - background revalidation interval
        expire: 86400,    // 1 day - hard expiration
      })
      return fetch('/api/data')
    }
    

    Cache Invalidation

    cacheTag() - Tag Cached Content

    import { cacheTag } from 'next/cache'
    
    async function getProducts() {
      'use cache'
      cacheTag('products')
      return db.products.findMany()
    }
    
    async function getProduct(id: string) {
      'use cache'
      cacheTag('products', `product-${id}`)
      return db.products.findUnique({ where: { id } })
    }
    

    updateTag() - Immediate Invalidation

    Use when you need the cache refreshed within the same request:

    'use server'
    
    import { updateTag } from 'next/cache'
    
    export async function updateProduct(id: string, data: FormData) {
      await db.products.update({ where: { id }, data })
      updateTag(`product-${id}`)  // Immediate - same request sees fresh data
    }
    

    revalidateTag() - Background Revalidation

    Use for stale-while-revalidate behavior:

    'use server'
    
    import { revalidateTag } from 'next/cache'
    
    export async function createPost(data: FormData) {
      await db.posts.create({ data })
      revalidateTag('posts')  // Background - next request sees fresh data
    }
    

    Runtime Data Constraint

    Cannot access cookies(), headers(), or searchParams inside use cache.

    Solution: Pass as Arguments

    // Wrong - runtime API inside use cache
    async function CachedProfile() {
      'use cache'
      const session = (await cookies()).get('session')?.value  // Error!
      return <div>{session}</div>
    }
    
    // Correct - extract outside, pass as argument
    async function ProfilePage() {
      const session = (await cookies()).get('session')?.value
      return <CachedProfile sessionId={session} />
    }
    
    async function CachedProfile({ sessionId }: { sessionId: string }) {
      'use cache'
      // sessionId becomes part of cache key automatically
      const data = await fetchUserData(sessionId)
      return <div>{data.name}</div>
    }
    

    Exception: use cache: private

    For compliance requirements when you can't refactor:

    async function getData() {
      'use cache: private'
      const session = (await cookies()).get('session')?.value  // Allowed
      return fetchData(session)
    }
    

    Cache Key Generation

    Cache keys are automatic based on:

    • Build ID - invalidates all caches on deploy
    • Function ID - hash of function location
    • Serializable arguments - props become part of key
    • Closure variables - outer scope values included
    async function Component({ userId }: { userId: string }) {
      const getData = async (filter: string) => {
        'use cache'
        // Cache key = userId (closure) + filter (argument)
        return fetch(`/api/users/${userId}?filter=${filter}`)
      }
      return getData('active')
    }
    

    Complete Example

    import { Suspense } from 'react'
    import { cookies } from 'next/headers'
    import { cacheLife, cacheTag } from 'next/cache'
    
    export default function DashboardPage() {
      return (
        <>
          {/* Static shell - instant from CDN */}
          <header><h1>Dashboard</h1></header>
          <nav>...</nav>
    
          {/* Cached - fast, revalidates hourly */}
          <Stats />
    
          {/* Dynamic - streams in with fresh data */}
          <Suspense fallback={<NotificationsSkeleton />}>
            <Notifications />
          </Suspense>
        </>
      )
    }
    
    async function Stats() {
      'use cache'
      cacheLife('hours')
      cacheTag('dashboard-stats')
    
      const stats = await db.stats.aggregate()
      return <StatsDisplay stats={stats} />
    }
    
    async function Notifications() {
      const userId = (await cookies()).get('userId')?.value
      const notifications = await db.notifications.findMany({
        where: { userId, read: false }
      })
      return <NotificationList items={notifications} />
    }
    

    Migration from Previous Versions

    Old Config Replacement
    experimental.ppr cacheComponents: true
    dynamic = 'force-dynamic' Remove (default behavior)
    dynamic = 'force-static' 'use cache' + cacheLife('max')
    revalidate = N cacheLife({ revalidate: N })
    unstable_cache() 'use cache' directive

    Limitations

    • Edge runtime not supported - requires Node.js
    • Static export not supported - needs server
    • Non-deterministic values (Math.random(), Date.now()) execute once at build time inside use cache

    For request-time randomness outside cache:

    import { connection } from 'next/server'
    
    async function DynamicContent() {
      await connection()  // Defer to request time
      const id = crypto.randomUUID()  // Different per request
      return <div>{id}</div>
    }
    

    Sources:

    • Cache Components Guide
    • use cache Directive
    Recommended Servers
    Svelte
    Svelte
    vastlint - IAB XML VAST validator and linter
    vastlint - IAB XML VAST validator and linter
    Astro Docs
    Astro Docs
    Repository
    f0rr0/f0rr0.github.io
    Files