Next.js App Router et Server Components

VérifiéSûr

Développez des applications Next.js 15 avec App Router, Server Components, Client Components, Server Actions et streaming. Impose l'utilisation de composants serveur par défaut, des schémas de récupération de données appropriés et le routage basé sur les fichiers. À appliquer lors de la création de pages, de la mise en œuvre de routes ou de l'optimisation des performances.

Spar Skills Guide Bot
DeveloppementIntermédiaire
15002/06/2026
Claude Code
#nextjs#app-router#server-components#client-components#performance

Recommandé pour

Notre avis

Fournit des directives pour construire des applications Next.js 15 avec App Router, Server Components, Client Components, Server Actions et le streaming.

Points forts

  • Applique les bonnes pratiques comme les Server Components par défaut
  • Couvre les modèles de récupération de données et les stratégies de mise en cache
  • Inclut un arbre de décision pour le choix du type de composant
  • Aborde l'optimisation des performances avec le streaming et Suspense

Limites

  • Suppose Next.js 15 et une compatibilité d'outils spécifique
  • Peut ne pas couvrir tous les cas limites de migration complexe
  • Ne fournit pas de modèles détaillés de gestion d'erreurs
Quand l'utiliser

Lors de la construction de nouvelles applications Next.js ou de la migration de Pages Router vers App Router avec des modèles modernes.

Quand l'éviter

Lors de la maintenance d'applications Pages Router existantes où l'adoption d'App Router n'est pas prévue.

Analyse de sécurité

Sûr
Score qualité92/100

The skill is purely instructional about Next.js patterns with no instructions to execute destructive commands, exfiltrate data, or bypass safety. The allowed-tools include Bash but the skill does not direct misuse.

Aucun point d'attention détecté

Exemples

Create a Server Component page with data fetching
Create a Next.js page using App Router that fetches data from an API and renders it as a Server Component with proper caching.
Implement an interactive client component
Implement a client component in Next.js that includes a form with validation and uses Server Actions to submit data.
Optimize a page with streaming and Suspense
Optimize a Next.js page by adding streaming with Suspense boundaries and parallel data fetching for faster initial load.

name: "Next.js App Router & Server Components" description: "Build Next.js 15 applications using App Router, Server Components, Client Components, Server Actions, and streaming. Apply when creating pages, handling data fetching, implementing routes, or optimizing performance." allowed-tools: Read, Write, Edit, Bash version: 1.1.0 compatibility: Claude Opus 4.5, Claude Code v2.x updated: 2026-01-24

Next.js App Router & Server Components

Systematic Next.js 15 development with App Router, Server Components, and performance optimization.

Overview

This Skill enforces:

  • Server Components by default (use client only when needed)
  • Proper data fetching patterns
  • File-based routing with App Router
  • Server Actions for mutations
  • Streaming and Suspense boundaries
  • Performance optimization
  • Route handlers and middleware

Apply when building Next.js pages, implementing routes, or optimizing performance.

Server vs Client Components

Default: Server Components (no directive needed) When needed: Client Components (add 'use client')

Decision Tree

Does component need browser APIs?
├─ YES → Client Component ('use client')
└─ NO → Server Component (default)

Does component need event handlers?
├─ YES → Client Component
└─ NO → Server Component

Does component need state or effects?
├─ YES → Client Component
└─ NO → Server Component

Does component fetch data?
├─ YES → Server Component (preferred)
└─ NO → Check other criteria

Server Components (Default)

Benefits

  • Runs on server only
  • Zero JavaScript sent to browser
  • Direct database access
  • Access to secrets
  • Better SEO
  • Faster initial load

Example

// app/users/page.tsx
// No 'use client' = Server Component
import { db } from '@/lib/db';

export default async function UsersPage() {
  // Direct database query (server-only)
  const users = await db.user.findMany();

  return (
    <div>
      <h1>Users</h1>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

Data Fetching in Server Components

// ✅ GOOD: Async component with await
export default async function Page() {
  const data = await fetch('https://api.example.com/data', {
    next: { revalidate: 3600 }  // Cache for 1 hour
  });
  const result = await data.json();

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

// ✅ GOOD: Parallel data fetching
export default async function Page() {
  const [users, posts] = await Promise.all([
    fetch('https://api.example.com/users').then(r => r.json()),
    fetch('https://api.example.com/posts').then(r => r.json())
  ]);

  return (
    <div>
      <Users data={users} />
      <Posts data={posts} />
    </div>
  );
}

// ❌ BAD: useEffect in Server Component
export default function Page() {
  useEffect(() => {
    // This doesn't work in Server Components!
    fetchData();
  }, []);
}

Client Components

When to Use

Add 'use client' when you need:

  • Event handlers (onClick, onChange, etc.)
  • State (useState, useReducer)
  • Effects (useEffect, useLayoutEffect)
  • Browser APIs (localStorage, window, navigator)
  • Custom hooks that use above
  • Interactive UI (modals, dropdowns, forms with validation)

Example

// app/components/Counter.tsx
'use client';

import { useState } from 'react';

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

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

Composing Server and Client

// app/page.tsx (Server Component)
import { Counter } from './components/Counter';  // Client Component

export default async function Page() {
  const data = await fetch('https://api.example.com/stats');
  const stats = await data.json();

  return (
    <div>
      <h1>Dashboard</h1>
      <p>Server-rendered stats: {stats.total}</p>
      
      {/* Client Component embedded in Server Component */}
      <Counter />
    </div>
  );
}

File-Based Routing

App Router Structure

app/
├── layout.tsx          # Root layout (wraps all pages)
├── page.tsx            # Home page (/)
├── about/
│   └── page.tsx        # About page (/about)
├── blog/
│   ├── page.tsx        # Blog list (/blog)
│   └── [slug]/
│       └── page.tsx    # Blog post (/blog/post-1)
├── dashboard/
│   ├── layout.tsx      # Nested layout
│   ├── page.tsx        # Dashboard (/dashboard)
│   └── settings/
│       └── page.tsx    # Settings (/dashboard/settings)
└── api/
    └── users/
        └── route.ts    # API route (/api/users)

Layouts

Root Layout (required):

// app/layout.tsx
export default function RootLayout({
  children
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <nav>Navigation</nav>
        {children}
        <footer>Footer</footer>
      </body>
    </html>
  );
}

Nested Layout:

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children
}: {
  children: React.ReactNode;
}) {
  return (
    <div>
      <aside>Sidebar</aside>
      <main>{children}</main>
    </div>
  );
}

Dynamic Routes

// app/blog/[slug]/page.tsx
export default async function BlogPost({
  params
}: {
  params: { slug: string };
}) {
  const post = await getPost(params.slug);

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

// Generate static params at build time
export async function generateStaticParams() {
  const posts = await getAllPosts();

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

Catch-All Routes

// app/docs/[...slug]/page.tsx
export default function DocsPage({
  params
}: {
  params: { slug: string[] };
}) {
  // /docs/a/b/c → params.slug = ['a', 'b', 'c']
  return <div>Docs: {params.slug.join('/')}</div>;
}

Server Actions

Form Mutations

// app/actions.ts
'use server';

import { revalidatePath } from 'next/cache';

export async function createUser(formData: FormData) {
  const name = formData.get('name') as string;
  const email = formData.get('email') as string;

  // Validate
  if (!name || !email) {
    return { error: 'Name and email required' };
  }

  // Create user
  await db.user.create({
    data: { name, email }
  });

  // Revalidate cache
  revalidatePath('/users');

  return { success: true };
}

Using Server Actions

// app/users/page.tsx
import { createUser } from './actions';

export default function UsersPage() {
  return (
    <form action={createUser}>
      <input name="name" placeholder="Name" />
      <input name="email" type="email" placeholder="Email" />
      <button type="submit">Create User</button>
    </form>
  );
}

With Client Component

// app/components/UserForm.tsx
'use client';

import { createUser } from '../actions';
import { useFormState } from 'react-dom';

export function UserForm() {
  const [state, formAction] = useFormState(createUser, null);

  return (
    <form action={formAction}>
      <input name="name" placeholder="Name" />
      <input name="email" type="email" placeholder="Email" />
      
      {state?.error && <p className="error">{state.error}</p>}
      {state?.success && <p className="success">User created!</p>}
      
      <button type="submit">Create User</button>
    </form>
  );
}

Streaming and Suspense

Streaming with Suspense

// app/dashboard/page.tsx
import { Suspense } from 'react';

async function SlowComponent() {
  await new Promise(resolve => setTimeout(resolve, 3000));
  return <div>Loaded after 3 seconds</div>;
}

export default function DashboardPage() {
  return (
    <div>
      <h1>Dashboard</h1>
      
      {/* Instant render */}
      <p>This loads immediately</p>
      
      {/* Streamed when ready */}
      <Suspense fallback={<div>Loading...</div>}>
        <SlowComponent />
      </Suspense>
    </div>
  );
}

Loading States

// app/dashboard/loading.tsx
export default function Loading() {
  return (
    <div>
      <p>Loading dashboard...</p>
    </div>
  );
}

Data Fetching Patterns

Caching Strategies

// ✅ GOOD: Cached (default)
const data = await fetch('https://api.example.com/data');

// ✅ GOOD: Revalidate every 1 hour
const data = await fetch('https://api.example.com/data', {
  next: { revalidate: 3600 }
});

// ✅ GOOD: No caching (always fresh)
const data = await fetch('https://api.example.com/data', {
  cache: 'no-store'
});

// ✅ GOOD: Tagged cache (revalidate by tag)
const data = await fetch('https://api.example.com/data', {
  next: { tags: ['users'] }
});

// Revalidate specific tag
import { revalidateTag } from 'next/cache';
revalidateTag('users');

Route Handlers (API Routes)

// app/api/users/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const users = await db.user.findMany();
  return NextResponse.json(users);
}

export async function POST(request: Request) {
  const body = await request.json();
  
  const user = await db.user.create({
    data: body
  });
  
  return NextResponse.json(user, { status: 201 });
}

Dynamic Route Handlers

// app/api/users/[id]/route.ts
export async function GET(
  request: Request,
  { params }: { params: { id: string } }
) {
  const user = await db.user.findUnique({
    where: { id: params.id }
  });

  if (!user) {
    return NextResponse.json(
      { error: 'User not found' },
      { status: 404 }
    );
  }

  return NextResponse.json(user);
}

Middleware

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // Check authentication
  const token = request.cookies.get('token');

  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/admin/:path*']
};

Anti-Patterns

// ❌ BAD: Client Component doing data fetching
'use client';
export default function Page() {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetch('/api/data').then(r => r.json()).then(setData);
  }, []);
}

// ✅ GOOD: Server Component fetches data
export default async function Page() {
  const data = await fetch('/api/data').then(r => r.json());
  return <div>{data}</div>;
}

// ❌ BAD: Entire page as Client Component
'use client';
export default function Page() {
  return <div>...</div>;
}

// ✅ GOOD: Only interactive parts as Client
export default function Page() {
  return (
    <div>
      <StaticContent />
      <InteractiveButton />  {/* This is 'use client' */}
    </div>
  );
}

// ❌ BAD: Passing Server Component to Client Component
<ClientComponent>
  <ServerComponent />
</ClientComponent>

// ✅ GOOD: Pass as children prop
<ClientComponent>
  {children}  {/* Server Component passed as children */}
</ClientComponent>

Verification Before Deployment

  • [ ] Server Components used by default
  • [ ] 'use client' only when necessary
  • [ ] Data fetching in Server Components
  • [ ] Server Actions for mutations
  • [ ] Streaming with Suspense boundaries
  • [ ] Caching strategy configured
  • [ ] Middleware for auth/redirects
  • [ ] Loading and error states
  • [ ] Route handlers for APIs
  • [ ] No unnecessary client bundles

Integration with Project Standards

Enforces Next.js best practices:

  • Performance optimization (streaming, caching)
  • Security (Server Components hide secrets)
  • Type safety (TypeScript throughout)
  • No unnecessary client JavaScript

Resources

  • Next.js 15 Docs: https://nextjs.org/docs
  • App Router: https://nextjs.org/docs/app
  • Server Components: https://nextjs.org/docs/app/building-your-application/rendering/server-components
  • Data Fetching: https://nextjs.org/docs/app/building-your-application/data-fetching

Last Updated: January 24, 2026 Compatibility: Claude Opus 4.5, Claude Code v2.x Status: Production Ready

January 2026 Update: This skill is compatible with Claude Opus 4.5 and Claude Code v2.x. For complex tasks, use the effort: high parameter for thorough analysis.

Skills similaires