← Back to list

nextjs-app-router-mastery
by autohandai
A collection of curated useful skills for autohand cli agent
⭐ 0🍴 0📅 Jan 23, 2026
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
Score
Total Score
60/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
✓最近の活動
1ヶ月以内に更新
+10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
○言語
プログラミング言語が設定されている
0/5
✓タグ
1つ以上のタグが設定されている
+5
Reviews
💬
Reviews coming soon
