Claude
Skills
Sign in
Back

nextjs-data-fetching

Included with Lifetime
$97 forever

Use when next.js data fetching patterns including SSG, SSR, and ISR. Use when building data-driven Next.js applications.

Web Dev

What this skill does


# Next.js Data Fetching

Master data fetching in Next.js with static generation, server-side
rendering, and incremental static regeneration.

## Fetch with Caching Strategies

```typescript
// Default: 'force-cache' (similar to SSG)
export default async function Page() {
  const data = await fetch('https://api.example.com/data');
  const json = await data.json();

  return <div>{json.title}</div>;
}

// No caching: 'no-store' (similar to SSR)
export default async function DynamicPage() {
  const data = await fetch('https://api.example.com/data', {
    cache: 'no-store'
  });
  const json = await data.json();

  return <div>{json.title}</div>;
}

// Revalidate every 60 seconds (ISR)
export default async function RevalidatedPage() {
  const data = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 }
  });
  const json = await data.json();

  return <div>{json.title}</div>;
}

// Per-route caching config
export const revalidate = 3600; // Revalidate every hour

export default async function Page() {
  const data = await fetch('https://api.example.com/data');
  return <div>{data.title}</div>;
}

// Dynamic rendering
export const dynamic = 'force-dynamic'; // Equivalent to cache: 'no-store'
export const dynamic = 'force-static';  // Equivalent to cache: 'force-cache'
export const dynamic = 'error';         // Error if dynamic functions used
export const dynamic = 'auto';          // Default behavior
```

## Static Generation with generateStaticParams

```typescript
// app/posts/[id]/page.tsx
interface Post {
  id: string;
  title: string;
  content: string;
}

// Generate static paths at build time
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json());

  return posts.map((post: Post) => ({
    id: post.id.toString()
  }));
}

export default async function Post({ params }: { params: { id: string } }) {
  const post = await fetch(`https://api.example.com/posts/${params.id}`, {
    next: { revalidate: 3600 } // Revalidate hourly
  }).then(r => r.json());

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

// Multiple dynamic segments
// app/shop/[category]/[product]/page.tsx
export async function generateStaticParams() {
  const categories = await getCategories();

  const paths = await Promise.all(
    categories.map(async (category) => {
      const products = await getProducts(category.slug);
      return products.map((product) => ({
        category: category.slug,
        product: product.slug
      }));
    })
  );

  return paths.flat();
}

export default async function Product({
  params
}: {
  params: { category: string; product: string }
}) {
  const product = await getProduct(params.category, params.product);

  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
    </div>
  );
}

// Limit static generation for large datasets
export const dynamicParams = true; // Default: generate on-demand
export const dynamicParams = false; // Return 404 for ungenerated paths

export async function generateStaticParams() {
  // Only generate top 100 posts at build time
  const posts = await getPosts({ limit: 100 });

  return posts.map((post) => ({
    id: post.id
  }));
}
```

## Time-Based Revalidation (ISR)

```typescript
// Page-level revalidation
export const revalidate = 60; // Revalidate every 60 seconds

export default async function Page() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json());

  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
        </article>
      ))}
    </div>
  );
}

// Per-request revalidation
export default async function Page() {
  const staticData = await fetch('https://api.example.com/static', {
    next: { revalidate: 3600 } // Cache for 1 hour
  }).then(r => r.json());

  const dynamicData = await fetch('https://api.example.com/dynamic', {
    next: { revalidate: 10 } // Cache for 10 seconds
  }).then(r => r.json());

  return (
    <div>
      <div>Static: {staticData.value}</div>
      <div>Dynamic: {dynamicData.value}</div>
    </div>
  );
}

// Mixed caching strategies
export default async function Dashboard() {
  // Never cache (always fresh)
  const liveData = await fetch('https://api.example.com/live', {
    cache: 'no-store'
  }).then(r => r.json());

  // Cache indefinitely (build-time only)
  const staticData = await fetch('https://api.example.com/static', {
    cache: 'force-cache'
  }).then(r => r.json());

  // Revalidate periodically
  const periodicData = await fetch('https://api.example.com/periodic', {
    next: { revalidate: 300 }
  }).then(r => r.json());

  return (
    <div>
      <LiveStats data={liveData} />
      <StaticContent data={staticData} />
      <PeriodicUpdates data={periodicData} />
    </div>
  );
}
```

## On-Demand Revalidation

```typescript
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const secret = request.nextUrl.searchParams.get('secret');

  // Verify secret token
  if (secret !== process.env.REVALIDATE_SECRET) {
    return NextResponse.json({ error: 'Invalid secret' }, { status: 401 });
  }

  const path = request.nextUrl.searchParams.get('path');

  if (path) {
    // Revalidate specific path
    revalidatePath(path);
    return NextResponse.json({ revalidated: true, path });
  }

  return NextResponse.json({ error: 'Missing path' }, { status: 400 });
}

// app/posts/[slug]/page.tsx - Using cache tags
export default async function Post({ params }: { params: { slug: string } }) {
  const post = await fetch(`https://api.example.com/posts/${params.slug}`, {
    next: { tags: ['posts', `post-${params.slug}`] }
  }).then(r => r.json());

  return <article>{post.content}</article>;
}

// app/api/revalidate-tag/route.ts
export async function POST(request: NextRequest) {
  const tag = request.nextUrl.searchParams.get('tag');

  if (tag) {
    revalidateTag(tag);
    return NextResponse.json({ revalidated: true, tag });
  }

  return NextResponse.json({ error: 'Missing tag' }, { status: 400 });
}

// Server Action for revalidation
'use server';
import { revalidatePath } from 'next/cache';

export async function updatePost(id: string, data: FormData) {
  await db.post.update({ where: { id }, data });

  // Revalidate the post page
  revalidatePath(`/posts/${id}`);

  // Revalidate the posts list
  revalidatePath('/posts');
}
```

## Parallel Data Fetching

```typescript
// Fetch multiple resources in parallel
export default async function Page() {
  const [posts, categories, tags] = await Promise.all([
    fetch('https://api.example.com/posts').then(r => r.json()),
    fetch('https://api.example.com/categories').then(r => r.json()),
    fetch('https://api.example.com/tags').then(r => r.json())
  ]);

  return (
    <div>
      <PostList posts={posts} />
      <CategoryList categories={categories} />
      <TagCloud tags={tags} />
    </div>
  );
}

// Parallel fetching with different cache strategies
export default async function Dashboard() {
  const [stats, recentActivity, settings] = await Promise.all([
    fetch('https://api.example.com/stats', {
      next: { revalidate: 60 }
    }).then(r => r.json()),

    fetch('https://api.example.com/activity', {
      cache: 'no-store'
    }).then(r => r.json()),

    fetch('https://api.example.com/settings', {
      cache: 'force-cache'
    }).then(r => r.json())
  ]);

  return (
    <div>
      <Stats data={stats} />
      <Activity data={recentActivity} />
      <Settings data={settings} />
    </div>
  );
}

// Fetching with fallbacks
export default async function Page() {
  const results = await Promise.allSettled([
    fetch('https://api.example.com/required').then(r => r.json()),
    fetch('https://api.example

Related in Web Dev