nextjs-code-review

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Next.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
typescript-software-architect-review
agent for deep architectural analysis when invoked through the agent system.
该技能为基于App Router构建的Next.js应用提供结构化、全面的代码评审服务,会对照Next.js最佳实践、React Server Component模式、缓存策略和生产就绪标准对代码进行评估。评审会输出可落地的问题结果,按严重程度(严重、警告、建议)分类,并附带具体的改进代码示例。
通过Agent系统调用该技能时,它会委托
typescript-software-architect-review
Agent进行深度架构分析。

When 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

使用说明

  1. Identify Scope: Determine which Next.js route segments and components are under review. Use
    glob
    to discover
    page.tsx
    ,
    layout.tsx
    ,
    loading.tsx
    ,
    error.tsx
    ,
    route.ts
    , and
    middleware.ts
    files.
  2. Analyze Component Boundaries: Verify proper Server Component / Client Component separation. Check that
    'use client'
    is placed only where necessary and as deep in the component tree as possible. Ensure Server Components don't import client-only modules.
  3. Review Data Fetching: Validate fetch patterns — check for proper
    cache
    and
    revalidate
    options, parallel data fetching with
    Promise.all
    , and avoidance of request waterfalls. Verify that server-side data fetching doesn't expose sensitive data to the client.
  4. Evaluate Caching Strategy: Review static vs dynamic rendering decisions. Check
    generateStaticParams
    usage for static generation,
    revalidatePath
    /
    revalidateTag
    for on-demand revalidation, and proper cache headers for API routes.
  5. Assess Server Actions: Review form actions for proper validation (both client and server-side), error handling, optimistic updates with
    useOptimistic
    , and security (ensure actions don't expose sensitive operations without authorization).
  6. Check Middleware: Review middleware for proper request matching, authentication/authorization logic, response modification, and performance impact. Verify it runs only on necessary routes.
  7. Review Metadata & SEO: Check
    generateMetadata
    functions, Open Graph tags, structured data,
    robots.txt
    , and
    sitemap.xml
    configurations. Verify dynamic metadata is properly implemented for pages with variable content.
  8. Produce Review Report: Generate a structured report with severity-classified findings (Critical, Warning, Suggestion), positive observations, and prioritized recommendations with code examples.
  1. 确定评审范围:明确需要评审的Next.js路由段和组件,使用
    glob
    匹配查找
    page.tsx
    layout.tsx
    loading.tsx
    error.tsx
    route.ts
    middleware.ts
    文件。
  2. 分析组件边界:验证Server Component/Client Component是否合理拆分,检查
    'use client'
    是否仅在必要位置声明、且尽可能放在组件树的深层,确保Server Component不会导入仅客户端可用的模块。
  3. 评审数据获取逻辑:校验fetch模式,检查
    cache
    revalidate
    配置是否合理,是否使用
    Promise.all
    实现并行数据获取、避免请求瀑布流,确保服务端数据获取不会将敏感数据暴露给客户端。
  4. 评估缓存策略:评审静态渲染/动态渲染的决策是否合理,检查静态生成场景下
    generateStaticParams
    的使用、按需重验证场景下
    revalidatePath
    /
    revalidateTag
    的使用,以及API路由的缓存头配置是否正确。
  5. 审核Server Actions:评审表单操作是否做了充分的客户端+服务端校验、错误处理,是否使用
    useOptimistic
    实现乐观更新,以及安全性是否达标(确保未授权情况下无法调用敏感操作)。
  6. 检查中间件逻辑:评审中间件的请求匹配规则、认证/授权逻辑、响应修改逻辑和性能影响,确保中间件仅在必要的路由上运行。
  7. 评审元数据与SEO:检查
    generateMetadata
    函数、Open Graph标签、结构化数据、
    robots.txt
    sitemap.xml
    配置,确保可变内容页面的动态元数据实现正确。
  8. 生成评审报告:输出结构化报告,包含按严重等级分类的问题(严重、警告、建议)、亮点观察,以及带代码示例的优先级改进建议。

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
    'use client'
    boundaries as deep in the tree as possible
  • Fetch data in Server Components — avoid client-side fetching for initial data
  • Use parallel data fetching (
    Promise.all
    ) to avoid request waterfalls
  • Implement proper loading, error, and not-found states for every route segment
  • Validate all Server Action inputs with Zod or similar libraries
  • Use
    revalidatePath
    /
    revalidateTag
    instead of time-based revalidation when possible
  • Scope middleware to specific routes with
    config.matcher
  • Implement
    generateMetadata
    for dynamic pages with variable content
  • Use
    generateStaticParams
    for static pages with known parameters
  • Avoid importing server-only code in Client Components — use the
    server-only
    package
  • 尽可能将
    '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
references/
directory for detailed review checklists and pattern documentation:
  • references/app-router-patterns.md
    — App Router best practices and patterns
  • references/server-components.md
    — Server Component and Client Component boundary guide
  • references/performance.md
    — Next.js performance optimization checklist
详细的评审检查清单和模式文档可查看
references/
目录:
  • references/app-router-patterns.md
    — App Router最佳实践和模式
  • references/server-components.md
    — Server Component与Client Component边界指南
  • references/performance.md
    — Next.js性能优化检查清单