Refactor high-complexity React components in Dify frontend. Use when `pnpm analyze-component...
npx skills add autohandai/community-skills --skill "nextjs-app-router-mastery"
Install specific skill from multi-skill repository
# Description
Next.js 14+ App Router patterns, server components, and data fetching
# SKILL.md
name: nextjs-app-router-mastery
description: Next.js 14+ App Router patterns, server components, and data fetching
license: MIT
compatibility: nextjs 14+, react 18+
allowed-tools: read_file write_file apply_patch search_with_context run_command
Next.js App Router Mastery
Core Concepts
- Server Components by Default - Components are server-rendered unless marked
'use client' - Streaming & Suspense - Progressive rendering with loading states
- Parallel Routes - Simultaneous route rendering
- 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
- Keep client components at the leaves of the tree
- Pass serializable props from server to client components
- Use
loading.tsxfor route-level loading states - Colocate data fetching with the component that uses it
- Use route groups
(group)for organization without affecting URL
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.