nextjs-app-router · diff
v1.0.0 to v1.0.1
84 added, 106 removed. Audit A to A.
---
name: nextjs-app-router
- description: '用于 Next.js App Router 模式开发,包含 RSC、Server Actions 和路由最佳实践。来源:skills.sh 10.2K installs。'
+ description: 'Master Next.js 14+ App Router with Server Components, streaming, parallel routes, and advanced data fetching. Use when building Next.js applications, implementing SSR/SSG, or optimizing React Server Components.'
zh_description: "用于 Next.js App Router 项目开发、路由设计和服务端渲染实践。"
- version: "1.0.0"
+ version: "1.0.1"
author: "seaworld008"
source: "skills.sh"
source_url: "https://skills.sh/wshobson/agents/nextjs-app-router-patterns"
license: "MIT"
tags: '["app", "development", "nextjs", "router"]'
created_at: "2026-03-27"
- updated_at: "2026-03-27"
+ updated_at: "2026-08-20"
quality: 4
complexity: "intermediate"
---
- # Next.js App Router
-
- ## 触发条件
- - 构建现代 Web 应用,需要高性能的 React Server Components (RSC) 渲染架构时。
- - 想要摆脱传统 API 路由模式,利用 Server Actions 直接在 UI 组件中处理表单提交和数据库变更时。
- - 实现复杂的多级路由嵌套、并行路由 (Parallel Routes) 或拦截路由 (Intercepting Routes) 时。
- - 需要利用 Next.js 14+ 的 Metadata API 优化 SEO 和动态社交卡片预览时。
- - 追求极致的加载体验,利用 Streaming、Suspense 和局部加载 (Loading UI) 减少用户等待感时。
- - 在页面级别细粒度控制缓存(Static vs Dynamic)、中间件 (Middleware) 和边缘运行时 (Edge Runtime) 时。
-
- ## 核心能力
+ # Next.js App Router Patterns
- ### 1. App Router vs Pages Router 对比
- - **文件系统**: `app/` 目录下默认所有组件为 Server Components,而 `pages/` 默认为 Client Components。
- - **渲染模型**: App Router 支持更细粒度的部分渲染(Partial Rendering),而 Pages Router 更多依赖于全页面的 SSR/SSG。
- - **布局流**: App Router 通过 `layout.tsx` 实现跨页面状态保持和 UI 共享,避免不必要的重新渲染。
+ Comprehensive patterns for Next.js 14+ App Router architecture, Server Components, and modern full-stack React development.
- ### 2. React Server Components (RSC) 最佳实践
- - **数据获取**: 在 Server Components 中直接使用 `async/await` 调用数据库(如 Prisma, Drizzle)或第三方 API,无需外部数据获取 Hook(如 SWR/React Query)。
- - **组件边界**: 明确区分 Client Component (`"use client"`) 和 Server Component。尽可能将状态、事件处理器和浏览器 API(如 `useEffect`, `window`)封装在小的 Client Component 中,保持大块 UI 为 Server Component 以减少 JS Bundle 体积。
+ ## When to Use This Skill
- ### 3. Server Actions (数据变更)
- - **表单处理**: 使用 `action` 属性绑定 Server Action,结合 `useFormStatus` 和 `useFormState` 处理 Loading 状态和验证反馈。
- - **安全性**: 默认具备 CSRF 防护。在 Action 内部执行身份验证 (`auth()`) 和权限校验。
- - **数据更新**: 完成操作后通过 `revalidatePath("/")` 或 `revalidateTag("collection")` 触发局部数据缓存刷新。
+ - Building new Next.js applications with App Router
+ - Migrating from Pages Router to App Router
+ - Implementing Server Components and streaming
+ - Setting up parallel and intercepting routes
+ - Optimizing data fetching and caching
+ - Building full-stack features with Server Actions
- ### 4. 路由进阶 (Advanced Routing)
- - **Parallel Routes**: 使用 `@folder` 语法在同一布局中同时显示多个独立页面(如仪表盘中同时展示统计表和通知列表)。
- - **Intercepting Routes**: 使用 `(..)` 语法在当前上下文拦截路由(如点击图片在 Modal 中预览,但刷新页面则直接导航至详情页)。
- - **动态路由**: 熟练使用 `[id]`、`[[...slug]]` (Catch-all) 进行动态路径匹配。
+ ## Core Concepts
- ### 5. 流式传输与 Suspense (Streaming & Suspense)
- - **Loading UI**: 在目录中创建 `loading.tsx` 实现即时的骨架屏反馈。
- - **部分流式**: 使用 `<Suspense>` 包裹耗时的数据拉取组件,允许页面的非阻塞加载,优先显示已就绪的静态内容。
+ ### 1. Rendering Modes
- ### 6. Metadata API & SEO
- - **静态元数据**: 在 `layout.tsx` 或 `page.tsx` 中定义 `export const metadata` 对象。
- - **动态元数据**: 使用 `generateMetadata` 函数基于路由参数或数据动态生成页面标题、描述和 OpenGraph 图像。
+ | Mode | Where | When to Use |
+ | --------------------- | ------------ | ----------------------------------------- |
+ | **Server Components** | Server only | Data fetching, heavy computation, secrets |
+ | **Client Components** | Browser | Interactivity, hooks, browser APIs |
+ | **Static** | Build time | Content that rarely changes |
+ | **Dynamic** | Request time | Personalized or real-time data |
+ | **Streaming** | Progressive | Large pages, slow data sources |
- ### 7. 中间件配置 (Middleware)
- - **身份校验**: 拦截未授权访问并重定向至登录页。
- - **多语言 (i18n)**: 根据浏览器 Header 自动切换语言路径。
- - **匹配规则**: 在 `config.matcher` 中精确定义中间件执行的路径范围。
+ ### 2. File Conventions
- ## 常用命令/模板
+ ```
+ app/
+ ├── layout.tsx # Shared UI wrapper
+ ├── page.tsx # Route UI
+ ├── loading.tsx # Loading UI (Suspense)
+ ├── error.tsx # Error boundary
+ ├── not-found.tsx # 404 UI
+ ├── route.ts # API endpoint
+ ├── template.tsx # Re-mounted layout
+ ├── default.tsx # Parallel route fallback
+ └── opengraph-image.tsx # OG image generation
+ ```
- ### 基础项目初始化与检查
- ```bash
- # 启动本地开发服务 (带热更新)
- pnpm dev
+ ## Quick Start
- # 构建生产产物并分析体积 (Next.js Bundle Analyzer)
- ANALYZE=true pnpm build
+ ```typescript
+ // app/layout.tsx
+ import { Inter } from 'next/font/google'
+ import { Providers } from './providers'
- # 快速检查当前路由配置
- find app/ -name "page.tsx" -o -name "route.ts" | sort
+ const inter = Inter({ subsets: ['latin'] })
- # 检查 Next.js 版本并升级
- pnpm outdated next
- pnpm add next@latest react@latest react-dom@latest
- ```
+ export const metadata = {
+ title: { default: 'My App', template: '%s | My App' },
+ description: 'Built with Next.js App Router',
+ }
- ### Server Component 数据拉取模板
- ```tsx
- // app/users/[id]/page.tsx
- import { db } from "@/lib/db";
- import { Suspense } from "react";
- import UserSkeleton from "@/components/UserSkeleton";
+ export default function RootLayout({
+ children,
+ }: {
+ children: React.ReactNode
+ }) {
+ return (
+ <html lang="en" suppressHydrationWarning>
+ <body className={inter.className}>
+ <Providers>{children}</Providers>
+ </body>
+ </html>
+ )
+ }
- async function UserProfile({ id }: { id: string }) {
- const user = await db.user.findUnique({ where: { id } });
- if (!user) return <div>User not found</div>;
- return <h1>{user.name}</h1>;
+ // app/page.tsx - Server Component by default
+ async function getProducts() {
+ const res = await fetch('https://api.example.com/products', {
+ next: { revalidate: 3600 }, // ISR: revalidate every hour
+ })
+ return res.json()
}
- export default function Page({ params }: { params: { id: string } }) {
+ export default async function HomePage() {
+ const products = await getProducts()
+
return (
<main>
- <Suspense fallback={<UserSkeleton />}>
- <UserProfile id={params.id} />
- </Suspense>
+ <h1>Products</h1>
+ <ProductGrid products={products} />
</main>
- );
+ )
}
```
- ### Server Action 表单提交模板
- ```tsx
- // actions/user-actions.ts
- "use server";
-
- import { revalidatePath } from "next/cache";
-
- export async function updateUsername(formData: FormData) {
- const name = formData.get("username") as string;
- // 1. 权限校验 (例如使用 next-auth)
- // 2. 数据库更新 (例如 Prisma)
- await db.user.update({ data: { name } });
-
- // 3. 刷新缓存并重定向
- revalidatePath("/profile");
- }
- ```
+ ## Detailed patterns and worked examples
- ### 中间件权限保护模板
- ```typescript
- // middleware.ts
- import { NextResponse } from 'next/server';
- import type { NextRequest } from 'next/server';
+ Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient.
- export function middleware(request: NextRequest) {
- const token = request.cookies.get('next-auth.session-token');
+ ## Best Practices
- if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
- return NextResponse.redirect(new URL('/login', request.url));
- }
+ ### Do's
- return NextResponse.next();
- }
+ - **Start with Server Components** - Add 'use client' only when needed
+ - **Colocate data fetching** - Fetch data where it's used
+ - **Use Suspense boundaries** - Enable streaming for slow data
+ - **Leverage parallel routes** - Independent loading states
+ - **Use Server Actions** - For mutations with progressive enhancement
- export const config = {
- matcher: ['/dashboard/:path*', '/settings/:path*'],
- };
- ```
+ ### Don'ts
- ## 边界与限制
- - **客户端组件限制**: Client Components 不能包含 Server-only 库(如 `fs`, `pg`),必须通过 Props 传递数据。
- - **序列化限制**: 从 Server Component 传递给 Client Component 的 Props 必须是可序列化的(不能传递复杂的函数、Map、Set 等,除非使用 `use server`)。
- - **缓存复杂性**: App Router 的缓存机制(Request Memoization, Data Cache, Full Route Cache)较为复杂,需要理解 `force-dynamic` 等配置。
- - **第三方库兼容性**: 部分旧版 React 库可能不支持 RSC,需要手动添加 `"use client"` 或使用 Wrapper。
- - **边缘运行时限制**: Edge Runtime 不支持完整的 Node.js API,在进行大量加密、复杂计算或文件操作时需使用默认的 Node.js Runtime。
+ - **Don't pass serializable data** - Server → Client boundary limitations
+ - **Don't use hooks in Server Components** - No useState, useEffect
+ - **Don't fetch in Client Components** - Use Server Components or React Query
+ - **Don't over-nest layouts** - Each layout adds to the component tree
+ - **Don't ignore loading states** - Always provide loading.tsx or Suspense