nextjs-code-review
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNext.js Code Review
Next.js 代码评审
Overview
概述
This skill provides structured, comprehensive code review for Next.js applications built with the App Router. It evaluates code against Next.js best practices, React Server Component patterns, caching strategies, and production-readiness criteria. The review produces actionable findings categorized by severity (Critical, Warning, Suggestion) with concrete code examples for improvements.
This skill delegates to the agent for deep architectural analysis when invoked through the agent system.
typescript-software-architect-review该技能为基于App Router构建的Next.js应用提供结构化、全面的代码评审服务,会对照Next.js最佳实践、React Server Component模式、缓存策略和生产就绪标准对代码进行评估。评审会输出可落地的问题结果,按严重程度(严重、警告、建议)分类,并附带具体的改进代码示例。
通过Agent系统调用该技能时,它会委托 Agent进行深度架构分析。
typescript-software-architect-reviewWhen to Use
适用场景
- Reviewing Next.js pages, layouts, and route segments before merging
- Validating Server Component vs Client Component boundaries
- Checking Server Actions for security and correctness
- Reviewing data fetching patterns (fetch, cache, revalidation)
- Evaluating caching strategies (static generation, ISR, dynamic rendering)
- Assessing middleware implementations (authentication, redirects, rewrites)
- Reviewing API route handlers for proper request/response handling
- Validating metadata configuration for SEO
- Checking loading, error, and not-found page implementations
- After implementing new Next.js features or migrating from Pages Router
- 合并前评审Next.js页面、布局和路由段代码
- 校验Server Component与Client Component的边界划分是否合理
- 检查Server Actions的安全性和正确性
- 评审数据获取模式(fetch、缓存、重验证)
- 评估缓存策略(静态生成、ISR、动态渲染)
- 审核中间件实现(认证、重定向、重写)
- 评审API路由处理函数的请求/响应处理是否规范
- 校验SEO相关的元数据配置
- 检查加载页、错误页、404页的实现
- 实现新的Next.js功能或从Pages Router迁移后做代码校验
Instructions
使用说明
-
Identify Scope: Determine which Next.js route segments and components are under review. Useto discover
glob,page.tsx,layout.tsx,loading.tsx,error.tsx, androute.tsfiles.middleware.ts -
Analyze Component Boundaries: Verify proper Server Component / Client Component separation. Check thatis placed only where necessary and as deep in the component tree as possible. Ensure Server Components don't import client-only modules.
'use client' -
Review Data Fetching: Validate fetch patterns — check for properand
cacheoptions, parallel data fetching withrevalidate, and avoidance of request waterfalls. Verify that server-side data fetching doesn't expose sensitive data to the client.Promise.all -
Evaluate Caching Strategy: Review static vs dynamic rendering decisions. Checkusage for static generation,
generateStaticParams/revalidatePathfor on-demand revalidation, and proper cache headers for API routes.revalidateTag -
Assess Server Actions: Review form actions for proper validation (both client and server-side), error handling, optimistic updates with, and security (ensure actions don't expose sensitive operations without authorization).
useOptimistic -
Check Middleware: Review middleware for proper request matching, authentication/authorization logic, response modification, and performance impact. Verify it runs only on necessary routes.
-
Review Metadata & SEO: Checkfunctions, Open Graph tags, structured data,
generateMetadata, androbots.txtconfigurations. Verify dynamic metadata is properly implemented for pages with variable content.sitemap.xml -
Produce Review Report: Generate a structured report with severity-classified findings (Critical, Warning, Suggestion), positive observations, and prioritized recommendations with code examples.
-
确定评审范围:明确需要评审的Next.js路由段和组件,使用匹配查找
glob、page.tsx、layout.tsx、loading.tsx、error.tsx和route.ts文件。middleware.ts -
分析组件边界:验证Server Component/Client Component是否合理拆分,检查是否仅在必要位置声明、且尽可能放在组件树的深层,确保Server Component不会导入仅客户端可用的模块。
'use client' -
评审数据获取逻辑:校验fetch模式,检查和
cache配置是否合理,是否使用revalidate实现并行数据获取、避免请求瀑布流,确保服务端数据获取不会将敏感数据暴露给客户端。Promise.all -
评估缓存策略:评审静态渲染/动态渲染的决策是否合理,检查静态生成场景下的使用、按需重验证场景下
generateStaticParams/revalidatePath的使用,以及API路由的缓存头配置是否正确。revalidateTag -
审核Server Actions:评审表单操作是否做了充分的客户端+服务端校验、错误处理,是否使用实现乐观更新,以及安全性是否达标(确保未授权情况下无法调用敏感操作)。
useOptimistic -
检查中间件逻辑:评审中间件的请求匹配规则、认证/授权逻辑、响应修改逻辑和性能影响,确保中间件仅在必要的路由上运行。
-
评审元数据与SEO:检查函数、Open Graph标签、结构化数据、
generateMetadata和robots.txt配置,确保可变内容页面的动态元数据实现正确。sitemap.xml -
生成评审报告:输出结构化报告,包含按严重等级分类的问题(严重、警告、建议)、亮点观察,以及带代码示例的优先级改进建议。
Examples
示例
Example 1: Server/Client Component Boundaries
示例1:Server/Client Component边界
tsx
// ❌ Bad: Entire page marked as client when only a button needs interactivity
'use client';
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await fetch(`/api/products/${params.id}`);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<button onClick={() => addToCart(product.id)}>Add to Cart</button>
</div>
);
}
// ✅ Good: Server Component with isolated Client Component
// app/products/[id]/page.tsx (Server Component)
import { AddToCartButton } from './add-to-cart-button';
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const product = await getProduct(id);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<AddToCartButton productId={product.id} />
</div>
);
}
// app/products/[id]/add-to-cart-button.tsx (Client Component)
'use client';
export function AddToCartButton({ productId }: { productId: string }) {
return <button onClick={() => addToCart(productId)}>Add to Cart</button>;
}tsx
// ❌ 错误示例:仅按钮需要交互就将整个页面标记为客户端组件
'use client';
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await fetch(`/api/products/${params.id}`);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<button onClick={() => addToCart(product.id)}>Add to Cart</button>
</div>
);
}
// ✅ 正确示例:Server Component与独立Client Component拆分
// app/products/[id]/page.tsx (Server Component)
import { AddToCartButton } from './add-to-cart-button';
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const product = await getProduct(id);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<AddToCartButton productId={product.id} />
</div>
);
}
// app/products/[id]/add-to-cart-button.tsx (Client Component)
'use client';
export function AddToCartButton({ productId }: { productId: string }) {
return <button onClick={() => addToCart(productId)}>Add to Cart</button>;
}Example 2: Data Fetching Patterns
示例2:数据获取模式
tsx
// ❌ Bad: Sequential data fetching creates waterfall
export default async function DashboardPage() {
const user = await getUser();
const orders = await getOrders(user.id);
const analytics = await getAnalytics(user.id);
return <Dashboard user={user} orders={orders} analytics={analytics} />;
}
// ✅ Good: Parallel data fetching with proper Suspense boundaries
export default async function DashboardPage() {
const user = await getUser();
const [orders, analytics] = await Promise.all([
getOrders(user.id),
getAnalytics(user.id),
]);
return <Dashboard user={user} orders={orders} analytics={analytics} />;
}
// ✅ Even better: Streaming with Suspense for independent sections
export default async function DashboardPage() {
const user = await getUser();
return (
<div>
<UserHeader user={user} />
<Suspense fallback={<OrdersSkeleton />}>
<OrdersSection userId={user.id} />
</Suspense>
<Suspense fallback={<AnalyticsSkeleton />}>
<AnalyticsSection userId={user.id} />
</Suspense>
</div>
);
}tsx
// ❌ 错误示例:串行数据获取产生请求瀑布流
export default async function DashboardPage() {
const user = await getUser();
const orders = await getOrders(user.id);
const analytics = await getAnalytics(user.id);
return <Dashboard user={user} orders={orders} analytics={analytics} />;
}
// ✅ 正确示例:使用Promise.all实现并行数据获取
export default async function DashboardPage() {
const user = await getUser();
const [orders, analytics] = await Promise.all([
getOrders(user.id),
getAnalytics(user.id),
]);
return <Dashboard user={user} orders={orders} analytics={analytics} />;
}
// ✅ 更优方案:配合Suspense实现独立模块流式渲染
export default async function DashboardPage() {
const user = await getUser();
return (
<div>
<UserHeader user={user} />
<Suspense fallback={<OrdersSkeleton />}>
<OrdersSection userId={user.id} />
</Suspense>
<Suspense fallback={<AnalyticsSkeleton />}>
<AnalyticsSection userId={user.id} />
</Suspense>
</div>
);
}Example 3: Server Actions Security
示例3:Server Actions安全性
tsx
// ❌ Bad: Server Action without validation or authorization
'use server';
export async function deleteUser(id: string) {
await db.user.delete({ where: { id } });
}
// ✅ Good: Server Action with validation, authorization, and error handling
'use server';
import { z } from 'zod';
import { auth } from '@/lib/auth';
import { revalidatePath } from 'next/cache';
const deleteUserSchema = z.object({ id: z.string().uuid() });
export async function deleteUser(rawData: { id: string }) {
const session = await auth();
if (!session || session.user.role !== 'admin') {
throw new Error('Unauthorized');
}
const { id } = deleteUserSchema.parse(rawData);
await db.user.delete({ where: { id } });
revalidatePath('/admin/users');
}tsx
// ❌ 错误示例:Server Action无校验和授权逻辑
'use server';
export async function deleteUser(id: string) {
await db.user.delete({ where: { id } });
}
// ✅ 正确示例:Server Action包含校验、授权和错误处理
'use server';
import { z } from 'zod';
import { auth } from '@/lib/auth';
import { revalidatePath } from 'next/cache';
const deleteUserSchema = z.object({ id: z.string().uuid() });
export async function deleteUser(rawData: { id: string }) {
const session = await auth();
if (!session || session.user.role !== 'admin') {
throw new Error('Unauthorized');
}
const { id } = deleteUserSchema.parse(rawData);
await db.user.delete({ where: { id } });
revalidatePath('/admin/users');
}Example 4: Caching and Revalidation
示例4:缓存与重验证
tsx
// ❌ Bad: No cache control, fetches on every request
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return <PostList posts={posts} />;
}
// ✅ Good: Explicit caching with time-based revalidation
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600, tags: ['blog-posts'] },
}).then(r => r.json());
return <PostList posts={posts} />;
}
// Revalidation in Server Action
'use server';
export async function publishPost(data: FormData) {
await db.post.create({ data: parseFormData(data) });
revalidateTag('blog-posts');
}tsx
// ❌ 错误示例:无缓存控制,每次请求都重新拉取数据
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return <PostList posts={posts} />;
}
// ✅ 正确示例:显式配置基于时间的缓存重验证
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600, tags: ['blog-posts'] },
}).then(r => r.json());
return <PostList posts={posts} />;
}
// Server Action中的重验证逻辑
'use server';
export async function publishPost(data: FormData) {
await db.post.create({ data: parseFormData(data) });
revalidateTag('blog-posts');
}Example 5: Middleware Review
示例5:中间件评审
typescript
// ❌ Bad: Middleware runs on all routes including static assets
import { NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const session = request.cookies.get('session');
if (!session) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
// Missing config.matcher
// ✅ Good: Scoped middleware with proper matcher
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const session = request.cookies.get('session');
if (!session) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*'],
};typescript
// ❌ 错误示例:中间件在所有路由(包括静态资源)上运行
import { NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const session = request.cookies.get('session');
if (!session) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
// 缺少config.matcher配置
// ✅ 正确示例:使用matcher限定中间件生效范围
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const session = request.cookies.get('session');
if (!session) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*'],
};Review Output Format
评审输出格式
Structure all code review findings as follows:
所有代码评审结果需按以下结构组织:
1. Summary
1. 概览
Brief overview with an overall quality score (1-10) and key observations.
包含整体质量评分(1-10分)和核心观察的简要说明。
2. Critical Issues (Must Fix)
2. 严重问题(必须修复)
Issues causing security vulnerabilities, data exposure, or broken functionality.
会导致安全漏洞、数据泄露或功能不可用的问题。
3. Warnings (Should Fix)
3. 警告(建议修复)
Issues that violate best practices, cause performance problems, or reduce maintainability.
违反最佳实践、导致性能问题或降低可维护性的问题。
4. Suggestions (Consider Improving)
4. 优化建议(可考虑改进)
Improvements for code organization, performance, or developer experience.
可优化代码组织、性能或开发体验的改进点。
5. Positive Observations
5. 亮点观察
Well-implemented patterns and good practices to acknowledge.
值得肯定的优秀实现和最佳实践。
6. Recommendations
6. 改进建议
Prioritized next steps with code examples for the most impactful improvements.
按优先级排序的后续行动项,附带最高效改进方案的代码示例。
Best Practices
最佳实践
- Keep boundaries as deep in the tree as possible
'use client' - Fetch data in Server Components — avoid client-side fetching for initial data
- Use parallel data fetching () to avoid request waterfalls
Promise.all - Implement proper loading, error, and not-found states for every route segment
- Validate all Server Action inputs with Zod or similar libraries
- Use /
revalidatePathinstead of time-based revalidation when possiblerevalidateTag - Scope middleware to specific routes with
config.matcher - Implement for dynamic pages with variable content
generateMetadata - Use for static pages with known parameters
generateStaticParams - Avoid importing server-only code in Client Components — use the package
server-only
- 尽可能将声明放在组件树的最深层
'use client' - 优先在Server Components中获取数据,初始数据避免走客户端拉取
- 使用实现并行数据获取,避免请求瀑布流
Promise.all - 为每个路由段实现完整的加载、错误、404状态
- 使用Zod或同类库校验所有Server Action的输入参数
- 优先使用/
revalidatePath而非基于时间的重验证revalidateTag - 使用限定中间件的生效路由范围
config.matcher - 为动态内容页面实现
generateMetadata - 已知参数的静态页面使用生成
generateStaticParams - 避免在Client Components中导入仅服务端可用的代码,可使用包做校验
server-only
Constraints and Warnings
约束与注意事项
- This skill targets Next.js App Router — Pages Router patterns may differ significantly
- Respect the project's Next.js version — some features are version-specific
- Do not suggest migrating from Pages Router to App Router unless explicitly requested
- Caching behavior differs between development and production — validate in production builds
- Server Actions must never expose sensitive operations without proper authentication checks
- Focus on high-confidence issues — avoid false positives on style preferences
- 该技能仅适配Next.js App Router,Pages Router的模式差异较大可能不适用
- 需适配项目的Next.js版本,部分功能存在版本差异
- 除非用户明确要求,否则不要建议从Pages Router迁移到App Router
- 开发环境和生产环境的缓存行为存在差异,需在生产构建中验证
- Server Action必须做权限校验,绝对不能暴露未授权即可调用的敏感操作
- 聚焦高置信度的问题,避免将代码风格偏好作为问题输出
References
参考资料
See the directory for detailed review checklists and pattern documentation:
references/- — App Router best practices and patterns
references/app-router-patterns.md - — Server Component and Client Component boundary guide
references/server-components.md - — Next.js performance optimization checklist
references/performance.md
详细的评审检查清单和模式文档可查看目录:
references/- — App Router最佳实践和模式
references/app-router-patterns.md - — Server Component与Client Component边界指南
references/server-components.md - — Next.js性能优化检查清单
references/performance.md