Get the FREE Ultimate OpenClaw Setup Guide →

nextjs-app-router-mastery

Scanned
npx machina-cli add skill autohandai/community-skills/nextjs-app-router-mastery --openclaw
Files (1)
SKILL.md
3.5 KB

Next.js App Router Mastery

Core Concepts

  1. Server Components by Default - Components are server-rendered unless marked 'use client'
  2. Streaming & Suspense - Progressive rendering with loading states
  3. Parallel Routes - Simultaneous route rendering
  4. Intercepting Routes - Modal patterns without navigation

File Conventions

app/
  layout.tsx          # Root layout (required)
  page.tsx            # Route UI
  loading.tsx         # Loading UI (Suspense boundary)
  error.tsx           # Error boundary
  not-found.tsx       # 404 UI
  route.ts            # API route handler
  template.tsx        # Re-renders on navigation
  default.tsx         # Parallel route fallback

Data Fetching Patterns

Server Component Fetching

// app/posts/page.tsx - Server Component
async function PostsPage() {
  const posts = await fetchPosts(); // Direct fetch, no useEffect
  return <PostList posts={posts} />;
}

Parallel Data Fetching

async function Dashboard() {
  // Parallel fetches - don't await sequentially
  const [user, posts, analytics] = await Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchAnalytics(),
  ]);
  return <DashboardView user={user} posts={posts} analytics={analytics} />;
}

Streaming with Suspense

export default function Page() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Suspense fallback={<StatsSkeleton />}>
        <Stats /> {/* Async component */}
      </Suspense>
      <Suspense fallback={<ChartSkeleton />}>
        <Chart /> {/* Streams in when ready */}
      </Suspense>
    </div>
  );
}

Server Actions

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

import { revalidatePath } from 'next/cache';

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string;
  await db.posts.create({ data: { title } });
  revalidatePath('/posts');
}

Route Handlers

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

export async function GET(request: Request) {
  const posts = await fetchPosts();
  return NextResponse.json(posts);
}

export async function POST(request: Request) {
  const body = await request.json();
  const post = await createPost(body);
  return NextResponse.json(post, { status: 201 });
}

Caching Strategies

// Force dynamic rendering
export const dynamic = 'force-dynamic';

// Revalidate every 60 seconds
export const revalidate = 60;

// Static generation
export const dynamic = 'force-static';

// Per-fetch revalidation
fetch(url, { next: { revalidate: 3600 } });

// On-demand revalidation
revalidatePath('/posts');
revalidateTag('posts');

Metadata

// Static metadata
export const metadata: Metadata = {
  title: 'My App',
  description: 'App description',
};

// Dynamic metadata
export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await fetchPost(params.id);
  return { title: post.title };
}

Best Practices

  1. Keep client components at the leaves of the tree
  2. Pass serializable props from server to client components
  3. Use loading.tsx for route-level loading states
  4. Colocate data fetching with the component that uses it
  5. Use route groups (group) for organization without affecting URL

Source

git clone https://github.com/autohandai/community-skills/blob/main/nextjs-app-router-mastery/SKILL.mdView on GitHub

Overview

Learn server components by default, streaming with Suspense, and parallel routes. This skill covers file conventions, data-fetching patterns, server actions, route handlers, caching strategies, and SEO metadata to build fast, scalable apps.

How This Skill Works

In Next.js 14+, pages live under app/ with Server Components rendered by default unless marked 'use client'. Data fetching happens directly in server components, enabling parallel fetches with Promise.all and streaming sections via Suspense. Server Actions, route handlers, and caching controls like revalidate and revalidatePath link frontend behavior to backend logic for robust UX and SEO.

When to Use It

  • Build a dashboard that fetches user, posts, and analytics in parallel for faster page loads.
  • Implement modal-like routes that overlay content without full navigation.
  • Enable progressive loading for long components using Suspense and streaming data.
  • Mutate data with server actions and ensure UI reflects changes via revalidation.
  • Generate static or dynamic metadata for SEO on dynamic routes.

Quick Start

  1. Step 1: Scaffold app structure under app/ (layout.tsx, page.tsx, loading.tsx, error.tsx, not-found.tsx).
  2. Step 2: Implement server components that fetch data directly and use Promise.all for parallel data. Wrap async sections with Suspense where appropriate.
  3. Step 3: Add server actions and route handlers, configure caching (dynamic, revalidate) and implement metadata generation with generateMetadata.

Best Practices

  • Keep client components at the leaves of the component tree to minimize re-renders.
  • Pass serializable props from server to client components to avoid serialization errors.
  • Use loading.tsx for route-level loading states to improve perceived performance.
  • Colocate data fetching with the component that uses it to keep concerns localized.
  • Use route groups (group) for organization without affecting the URL structure.

Example Use Cases

  • Dashboard page that uses Promise.all to fetch user, posts, and analytics, then renders a unified view.
  • Admin API for posts with app/api/posts/route.ts implementing GET and POST with NextResponse.
  • Streaming UI where Stats and Chart load via Suspense with skeleton fallbacks.
  • Server action to create a post (use server) and revalidatePath('/posts') to reflect changes.
  • Dynamic metadata generation for posts using generateMetadata based on fetchPost(params.id).

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers