Skip to content
data-fetching logo

Data Fetching

data-fetching

Best practices and conventions for server-side data fetching, caching, and rendering in Next.js 16+ applications.

SKILL.md

Full skill instructions

Overview

This skill covers server-side data fetching and caching patterns using Next.js 16+ Cache Components approach with Partial Prerendering (PPR). It combines fine-grained caching control with server-side data fetching for optimal performance.

Core Principles

Data Access Rules

  • NEVER call Drizzle ORM directly - Always use server actions defined in lib/​actions/
  • Cache at the component level - Use the use cache directive in pages/​layouts, not in action files
  • Wrap dynamic content - Use Suspense boundaries to separate static and dynamic content
  • Use lifetime profiles - Always specify cacheLife() with appropriate profile

Cache Components Workflow

1. Planning Data Fetching

Before implementing:

  • Identify what data is needed for the page/​component
  • Determine what content should be instantly visible (cached) vs. what can stream (dynamic)
  • Locate the appropriate server actions in lib/​actions/ or create new ones if needed
  • Plan cache tags for data that needs manual invalidation

2. Implementing Cached Data Fetching

Follow this pattern in pages or layouts:

import { cacheLife } from 'next/​cache'
import { getModels } from '@/​lib/​actions/​models'

export default async function ModelsPage() {
  'use cache'
  cacheLife('hours')

  const models = await getModels()

  return <div>{/​* render models */​}</​div>
}

3. Handling Dynamic Content

For runtime-dependent data (cookies, headers, searchParams):

import { Suspense } from 'react'

export default function Page() {
  return (
    <>
      <h1>Static Content</​h1>
      <Suspense fallback={<Skeleton />}>
        <DynamicUserContent />
      </​Suspense>
    </>
  )
}

async function DynamicUserContent() {
  const session = await getSession() // uses cookies()
  return <div>{session.user.name}</​div>
}

Caching Configuration

Cache Life Profiles

Use built-in lifetime profiles with cacheLife():

ProfileUse CaseDuration
'seconds'Highly volatile data~30 seconds
'minutes'Frequently updated content~5 minutes
'hours'Semi-static content~1 hour
'days'Mostly static content~1 day
'weeks'Rarely changing content~1 week
'max'Static contentMaximum duration

Default Choice: Use 'hours' for most content unless you have specific requirements.

Cache Tags and Revalidation

Using cacheTag for Manual Invalidation

Tag cached data that needs to be invalidated on specific events:

import { cacheLife, cacheTag } from 'next/​cache'
import { getModelById } from '@/​lib/​actions/​models'

export default async function ModelPage({ params }: { params: { id: string } }) {
  'use cache'
  cacheLife('hours')
  cacheTag('models', `model-${params.id}`)

  const model = await getModelById(params.id)
  return <div>{/​* render model */​}</​div>
}
Invalidating Cache with updateTag

Use in server actions for immediate cache expiration (read-your-own-writes):

'use server'
import { updateTag } from 'next/​cache'

export async function updateModel(id: string, data: ModelData) {
  // Update database via action
  await updateModelAction(id, data)

  // Immediately expire cache so user sees fresh data
  updateTag(`model-${id}`, 'models')
}
Using revalidateTag for Background Refresh

For stale-while-revalidate pattern:

'use server'
import { revalidateTag } from 'next/​cache'

export async function createModel(data: ModelData) {
  await createModelAction(data)

  // Stale-while-revalidate: serve stale, refresh in background
  revalidateTag('models', 'max')
}

Best Practices

Caching Strategy

  • Cache pages/​layouts, not actions - Add use cache directive in pages/​layouts that consume actions, never in action files themselves
  • Wrap actions in cached functions - The page/​layout function itself becomes the caching boundary
  • Use Suspense boundaries - Separate static shell from dynamic/​streaming content
  • Tag strategically - Use cache tags for content that changes infrequently but needs manual updates

Performance Optimization

  • Minimize dynamic APIs - Avoid using cookies(), headers(), or searchParams in cached functions
  • Parallel data fetching - Multiple server actions can be called in parallel within a cached component
  • Appropriate cache lifetimes - Balance freshness needs with server load

Data Mutation Patterns

  • Use updateTag for user mutations - When users need to see their changes immediately
  • Use revalidateTag for background updates - When serving slightly stale data is acceptable
  • Tag hierarchies - Use multiple tags (e.g., 'models' and 'model-123') for flexible invalidation

Common Patterns

Pattern 1: Cached List Page

import { cacheLife, cacheTag } from 'next/​cache'
import { getModels } from '@/​lib/​actions/​models'

export default async function ModelsPage() {
  'use cache'
  cacheLife('hours')
  cacheTag('models')

  const models = await getModels()
  return <div>{/​* render list */​}</​div>
}

Pattern 2: Cached Detail Page with Params

import { cacheLife, cacheTag } from 'next/​cache'
import { getModelById } from '@/​lib/​actions/​models'

export default async function ModelPage({ params }: { params: { id: string } }) {
  'use cache'
  cacheLife('hours')
  cacheTag('models', `model-${params.id}`)

  const model = await getModelById(params.id)
  return <div>{/​* render detail */​}</​div>
}

Pattern 3: Mixed Static and Dynamic Content

import { Suspense } from 'react'
import { cacheLife } from 'next/​cache'

export default function Page() {
  return (
    <>
      <StaticContent />
      <Suspense fallback={<LoadingSkeleton />}>
        <DynamicContent />
      </​Suspense>
    </>
  )
}

async function StaticContent() {
  'use cache'
  cacheLife('hours')

  const data = await getStaticData()
  return <div>{/​* render */​}</​div>
}

async function DynamicContent() {
  const session = await getSession() // uses cookies
  const userData = await getUserData(session.userId)
  return <div>{/​* render */​}</​div>
}

Pattern 4: Server Action with Cache Invalidation

'use server'
import { updateTag } from 'next/​cache'
import { updateModelAction } from '@/​lib/​actions/​models'

export async function updateModel(id: string, data: FormData) {
  const result = await updateModelAction(id, data)

  if (result.status === 'success') {
    // Immediately expire cache for this specific model and all models
    updateTag(`model-${id}`, 'models')
  }

  return result
}

Important Constraints

Serialization Requirements

  • Arguments must be serializable - Pass primitives, plain objects, and arrays only
  • No class instances or functions - Cannot pass non-serializable values as arguments to cached functions
  • Unserializable return values are OK - Can return React components or other unserializable values if you don't introspect them

What NOT to Cache

  • Functions using runtime APIs - cookies(), headers(), searchParams should not be in cached functions
  • Server Actions - Never add use cache to server action files; cache at the consumption point
  • Highly personalized content - User-specific data that varies per request

Configuration

Enable Cache Components in next.config.ts:

const nextConfig = {
  cacheComponents: true,
}

export default nextConfig

Troubleshooting

Cache Not Working

  • Verify cacheComponents: true is set in next.config.ts
  • Check that use cache is at the top of the function body
  • Ensure you're using Node.js runtime (Edge Runtime not supported)
  • Verify function arguments are serializable

Stale Data Issues

  • Check cache lifetime profile - may need shorter duration
  • Use updateTag instead of revalidateTag for immediate updates
  • Verify cache tags match between caching and invalidation

Performance Issues

  • Profile which content needs to be cached vs. dynamic
  • Use more Suspense boundaries to improve streaming
  • Consider longer cache lifetimes for stable content
  • Review database query performance in server actions

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View