Claude
Skills
Sign in
Back

nextjs-development

Included with Lifetime
$97 forever

Comprehensive Next.js development skill covering App Router, Server Components, data fetching, routing patterns, API routes, middleware, and full-stack Next.js applications

frontendnextjsreactapp-routerserver-componentsdata-fetchingroutingapi-routesmiddleware

What this skill does


# Next.js Development Skill

This skill provides comprehensive guidance for building modern Next.js applications using the App Router, Server Components, data fetching patterns, routing, API routes, middleware, and full-stack development techniques based on official Next.js documentation.

## When to Use This Skill

Use this skill when:
- Building full-stack React applications with server-side rendering (SSR)
- Creating static sites with incremental static regeneration (ISR)
- Developing modern web applications with React Server Components
- Building API backends with serverless route handlers
- Implementing SEO-optimized applications with metadata and Open Graph
- Creating production-ready web applications with built-in optimization
- Building e-commerce, blogs, dashboards, or content-driven sites
- Implementing authentication, data fetching, and complex routing patterns
- Optimizing images, fonts, and performance automatically
- Deploying serverless applications with edge computing capabilities

## Core Concepts

### App Router

The App Router is Next.js's modern routing system built on React Server Components. It uses the `app` directory for file-based routing with enhanced features.

**Basic App Structure:**
```
app/
├── layout.tsx          # Root layout (required)
├── page.tsx            # Home page
├── loading.tsx         # Loading UI
├── error.tsx           # Error UI
├── not-found.tsx       # 404 page
├── about/
│   └── page.tsx        # /about route
└── blog/
    ├── page.tsx        # /blog route
    ├── [slug]/
    │   └── page.tsx    # /blog/[slug] dynamic route
    └── layout.tsx      # Blog layout
```

**Root Layout (Required):**
```tsx
// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
```

**Page Component:**
```tsx
// app/page.tsx
export default function HomePage() {
  return (
    <main>
      <h1>Welcome to Next.js</h1>
      <p>Building modern web applications</p>
    </main>
  )
}
```

### Server Components

Server Components are React components that render on the server. They are the default in the App Router and provide better performance.

**Server Component (Default):**
```tsx
// app/posts/page.tsx
async function getPosts() {
  const res = await fetch('https://api.example.com/posts', {
    cache: 'force-cache' // Static generation
  })
  return res.json()
}

export default async function PostsPage() {
  const posts = await getPosts()

  return (
    <div>
      <h1>Blog Posts</h1>
      {posts.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  )
}
```

**Client Component (When Needed):**
```tsx
'use client'

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  )
}
```

**Mixing Server and Client Components:**
```tsx
// app/dashboard/page.tsx (Server Component)
import ClientCounter from './ClientCounter'

async function getData() {
  const res = await fetch('https://api.example.com/data')
  return res.json()
}

export default async function DashboardPage() {
  const data = await getData()

  return (
    <div>
      <h1>Dashboard</h1>
      <p>Server data: {data.value}</p>
      {/* Client component for interactivity */}
      <ClientCounter />
    </div>
  )
}
```

### Data Fetching

Next.js extends the native `fetch()` API with automatic caching and revalidation.

**Static Data Fetching (Default):**
```tsx
async function getStaticData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'force-cache' // Default, equivalent to getStaticProps
  })
  return res.json()
}

export default async function Page() {
  const data = await getStaticData()
  return <div>{data.title}</div>
}
```

**Dynamic Data Fetching:**
```tsx
async function getDynamicData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'no-store' // Equivalent to getServerSideProps
  })
  return res.json()
}

export default async function Page() {
  const data = await getDynamicData()
  return <div>{data.title}</div>
}
```

**Revalidation (ISR):**
```tsx
async function getRevalidatedData() {
  const res = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 } // Revalidate every 60 seconds
  })
  return res.json()
}

export default async function Page() {
  const data = await getRevalidatedData()
  return <div>{data.title}</div>
}
```

**Parallel Data Fetching:**
```tsx
async function getUser(id: string) {
  const res = await fetch(`https://api.example.com/users/${id}`)
  return res.json()
}

async function getUserPosts(id: string) {
  const res = await fetch(`https://api.example.com/users/${id}/posts`)
  return res.json()
}

export default async function UserPage({ params }: { params: { id: string } }) {
  // Fetch in parallel
  const [user, posts] = await Promise.all([
    getUser(params.id),
    getUserPosts(params.id)
  ])

  return (
    <div>
      <h1>{user.name}</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  )
}
```

**Sequential Data Fetching:**
```tsx
async function getUser(id: string) {
  const res = await fetch(`https://api.example.com/users/${id}`)
  return res.json()
}

async function getRecommendations(preferences: string[]) {
  const res = await fetch('https://api.example.com/recommendations', {
    method: 'POST',
    body: JSON.stringify({ preferences })
  })
  return res.json()
}

export default async function UserPage({ params }: { params: { id: string } }) {
  // First fetch user
  const user = await getUser(params.id)

  // Then fetch recommendations based on user data
  const recommendations = await getRecommendations(user.preferences)

  return (
    <div>
      <h1>{user.name}</h1>
      <h2>Recommendations</h2>
      <ul>
        {recommendations.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  )
}
```

### Routing

Next.js uses file-system based routing in the `app` directory.

**Dynamic Routes:**
```tsx
// app/blog/[slug]/page.tsx
export default function BlogPost({ params }: { params: { slug: string } }) {
  return <h1>Post: {params.slug}</h1>
}

// Generates static pages for these slugs at build time
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json())

  return posts.map((post) => ({
    slug: post.slug,
  }))
}
```

**Catch-All Routes:**
```tsx
// app/docs/[...slug]/page.tsx
export default function DocsPage({ params }: { params: { slug: string[] } }) {
  // /docs/a/b/c -> params.slug = ['a', 'b', 'c']
  return <h1>Docs: {params.slug.join('/')}</h1>
}
```

**Optional Catch-All Routes:**
```tsx
// app/shop/[[...slug]]/page.tsx
export default function ShopPage({ params }: { params: { slug?: string[] } }) {
  // /shop -> params.slug = undefined
  // /shop/clothes -> params.slug = ['clothes']
  // /shop/clothes/tops -> params.slug = ['clothes', 'tops']
  return <h1>Shop: {params.slug?.join('/') || 'All'}</h1>
}
```

**Route Groups:**
```
app/
├── (marketing)/          # Route group (not in URL)
│   ├── about/
│   │   └── page.tsx      # /about
│   └── contact/
│       └── page.tsx      # /contact
└── (shop)/
    ├── products/
    │   └── page.tsx      # /products
    └── cart/
        └── page.tsx      # /cart
```

**Parallel Routes:**
```
app/
├── @analytics/
│   └── page.tsx
├── @team/
│   └── page.tsx
└── layout.tsx

// app/layout.tsx
export default function Layout({
  children,
  analytics,
  team,
}: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}) {
  return (
    <>
      {children}
      {an

Related in frontend