一键导入
nextjs-patterns
Next.js App Router patterns, server actions, caching, and routing. Use when working with Next.js 14+ features.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Next.js App Router patterns, server actions, caching, and routing. Use when working with Next.js 14+ features.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when working with git operations including commits, branches, worktrees, and PRs. Covers the full git workflow from feature isolation to PR submission.
Complete PR submission pipeline with local sub-agent review before pushing, CI verification, and automated review integration. Always dispatches code-reviewer and code-simplifier for code changes, plus conditional reviewers (security, performance, dependency, accessibility, i18n, type-design, etc.) based on change type. Waits for CI with `gh pr checks --watch`. Integrates CodeRabbit and Greptile feedback.
GitHub CLI patterns for PR reviews, comments, and API operations. Use when working with gh api commands, especially for review threads and comments.
Use when following a written plan or task list. Checkpoint verification at each step. Triggers - execute plan, follow plan, implement plan, next step, continue, proceed.
Use for parallel branch development with workspace isolation.
Use when implementing analytics, event tracking, or setting up dashboards. Covers privacy-first tracking, event patterns, and common analytics tools.
| name | nextjs-patterns |
| description | Next.js App Router patterns, server actions, caching, and routing. Use when working with Next.js 14+ features. |
Decision guide for Next.js App Router patterns focusing on data fetching, caching, and server/client architecture.
Need data?
├─ Server Component → fetch() directly (default)
├─ Client interactivity needed → Server Action
├─ External API from client → Route Handler
└─ Real-time updates → Route Handler + streaming
// app/users/page.tsx - Server component (default)
async function UsersPage() {
// Direct async - no useEffect needed
const users = await db.user.findMany();
// Parallel fetching
const [users, stats] = await Promise.all([
db.user.findMany(),
db.stats.get(),
]);
return <UserList users={users} />;
}
// actions/user.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function createUser(formData: FormData) {
const data = Object.fromEntries(formData);
await db.user.create({ data });
revalidatePath('/users');
}
// Usage in component
<form action={createUser}>
<input name="email" />
<button type="submit">Create</button>
</form>
| Scenario | Cache Strategy | Implementation |
|---|---|---|
| Static data | Full cache | Default behavior |
| User-specific | No store | cache: 'no-store' |
| Periodic refresh | Time-based | revalidate: 3600 |
| On-demand | Tag-based | revalidateTag('users') |
// Time-based revalidation
const data = await fetch(url, { next: { revalidate: 3600 } });
// Tag-based revalidation
const data = await fetch(url, { next: { tags: ['users'] } });
// In server action
revalidateTag('users');
revalidatePath('/users');
app/
├── (marketing)/ # Route group (no URL segment)
│ ├── page.tsx # /
│ └── about/page.tsx # /about
├── (app)/
│ ├── layout.tsx # Shared app layout
│ └── dashboard/
│ ├── page.tsx # /dashboard
│ ├── loading.tsx # Loading UI
│ └── error.tsx # Error boundary
├── api/
│ └── users/
│ └── route.ts # /api/users
└── @modal/ # Parallel route for modals
└── (.)photo/[id]/ # Intercepting route
// loading.tsx - Automatic Suspense boundary
export default function Loading() {
return <Skeleton />;
}
// error.tsx - Must be client component
'use client';
export default function Error({ error, reset }: {
error: Error;
reset: () => void
}) {
return (
<div>
<p>Something went wrong</p>
<button onClick={reset}>Try again</button>
</div>
);
}
// not-found.tsx - Custom 404
export default function NotFound() {
return <p>Page not found</p>;
}
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Auth check
const token = request.cookies.get('token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Add headers
const response = NextResponse.next();
response.headers.set('x-custom-header', 'value');
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*'],
};
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Client-side fetch in Server Component | Defeats SSR | Use async/await directly |
'use client' on layout | Makes all children client | Push client boundary down |
Dynamic params without generateStaticParams | Slow builds | Define static params |
| Fetch in useEffect | Race conditions, waterfalls | Server component or action |
| Ignoring cache defaults | Over-fetching | Understand cache behavior |
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
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 });
}
router.push for form submissions (use server actions)revalidate: 0 everywhere (understand caching first)dynamic = 'force-dynamic' without justification// Generate metadata
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const product = await getProduct(params.id);
return { title: product.name };
}
// Static params for dynamic routes
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map(post => ({ slug: post.slug }));
}
// Streaming with Suspense
<Suspense fallback={<Loading />}>
<SlowComponent />
</Suspense>