跳到主要内容
一份给 AI 的 React/Next.js 开发规范:30 条规则,防止它乱写代码
景行景行

一份给 AI 的 React/Next.js 开发规范:30 条规则,防止它乱写代码

来源:prompts.chat 发布时间:2026-07-21

一份给 AI 的 React/Next.js 开发规范。

它解决的是:用 AI 写 React/Next.js 项目时,AI 经常乱来——乱加依赖、乱建抽象、把状态放错地方、不跑检查就说完工了。

30 个章节覆盖开发全流程:

  • 核心原则:先读项目、找可复用的、最小改动、端到端实现、跑检查再报
  • React vs Next.js 的选择
  • 默认技术栈和检查命令
  • 架构:src/ 布局、FSD-like 目录结构、导入方向
  • 公共 API、TypeScript、React 状态和副作用
  • Next.js 边界:Server/Client Components
  • 运行时校验和数据迁移、表单
  • shadcn/ui 和共享 UI、UI 表面选择、紧凑 UI
  • Overlay、菜单选项列表、长文本和溢出
  • Tailwind 版本验证、布局和侧边栏折叠
  • 浏览器 API 和 localStorage、工具间关系、统一领域操作
  • 无障碍、加载/空/错误状态、安全、性能
  • 用真实内容测试设计、变更后检查、Git、最终报告

它的核心价值:让 AI 写出更靠谱的代码,而不是一堆互不相连的组件。

适合谁:

  • 做前端、做全栈、用 React/Next.js 的科研小组成员
  • 用 AI 写代码、但经常要收拾烂摊子的人
  • 想让 AI 遵守一套开发规范的人

用法: 把这个文件放在项目根目录,命名为 AGENTS.md、CLAUDE.md 或 PROJECT_RULES.md,或作为 AI 的基础指令集。

提示词

# React / Next.js 项目通用规范

> 用途:用 React + TypeScript、Next.js + TypeScript、Tailwind CSS 开发各类项目的通用规则。
> 用法:把这个文件放在新项目根目录,命名为 AGENTS.md、CLAUDE.md 或 PROJECT_RULES.md,或作为 AI 智能体的基础指令集。
> 重要:这些指令不包含产品特定规则。把单个项目相关的内容放在单独的 PROJECT_RULES.md 文件里。

---

# 1. 核心原则

构建一个生产就绪的应用,而不是一堆互不相连的组件。

始终遵循这个顺序:

1. 先看当前项目结构、package.json、路由、UI 原语、stores、hooks、schemas 和项目规则。
2. 找已有的 actions、helpers、schemas、组件,能复用就复用。
3. 找出完成任务所需的最小改动。
4. 保留已有行为。
5. 每个新功能端到端实现:model、validation、UI、storage/import/export、边界情况、验证。
6. 跑相关检查,诚实报告结果。

不要加依赖、抽象、全局 store 或架构层,除非真的必要。
UI 默认用 shadcn/ui。没有明确理由,不要在它上面再加另一个 UI 库。

---

# 2. React 和 Next.js 怎么选

项目需要这些时用 Next.js:
- 路由
- SEO
- SSR / Server Components
- Server Actions
- Route Handlers / API 路由
- 认证
- 数据库访问
- 私有环境变量
- 内容发布

用 React + Vite 当:
- 应用完全客户端
- 不需要 SEO
- 它是本地工具、仪表盘、编辑器、管理面板或桌面类 UI
- 服务端已经作为独立服务存在

不要因为流行就选 Next.js。不要没有具体理由就加 Redux、Zustand、React Query、表单库或另一个 UI 库。

---

# 3. 默认技术栈和检查

默认用:
- React
- TypeScript strict 模式
- Tailwind CSS
- shadcn/ui 作为必需的 UI 方案
- Lucide React 或当前 shadcn 配置用的图标库
- ESLint
- 共享的 cn() helper
- 外部数据的运行时校验
- 无障碍 HTML 元素

用 shadcn/ui 作为 UI 原语的主要来源:按钮、输入、选择、对话框、抽屉、下拉、提示、标签页、轮播、卡片、徽章、骨架屏、滚动区域等。只有 shadcn 没有合适组件时才创建自定义原语。

MVP 先用 mock/JSON/localStorage 数据,先验证本地用户流程。后端、数据库、支付、认证、外部集成最后加。

改完代码至少跑:
npm run typecheck
npm run lint
npm run build

没跑这些命令或跑出错误,不要说项目能工作。

---

# 4. 架构

预期会增长的 Next.js 项目,默认把源码放在 src/ 里:src/app、src/components、src/lib、src/data、src/hooks、src/features。根级支持文件夹(public、配置文件、lockfile、README)留在项目根。

小项目可以这样:
src/
  app/ 或 pages/
  components/
  features/
  lib/
  shared/

中大型项目用 FSD-like:
src/
  app/       # bootstrap、providers、layouts、routes
  views/     # 页面级组合
  widgets/   # 大 UI 块
  features/  # 用户工作流
  entities/  # 领域模型
  shared/    # 通用 helpers、config、shadcn/ui 的薄包装

导入方向:
app/views -> widgets -> features -> entities -> shared

不要:
- 把 widgets 导进 features
- 把业务逻辑放 shared
- 把 shared/lib 变成不相关函数的垃圾场
- 在多个 UI 组件里重复 mutation 逻辑
- 当模块暴露公共 API 时,深层导入它的内部

---

# 5. 公共 API

每个 feature、entity 或 shared UI 文件夹,对外使用时应该通过 index.ts 暴露清晰的公共 API。shadcn 原语的公共 API 通常在 components/ui/* 或项目本地 UI 层。

好:
import { createTask } from "@/features/create-task";

坏:
import { createTask } from "@/features/create-task/model/createTask";

例外:同一个 feature 或 entity 内部的代码。

---

# 6. TypeScript

要求:
- 开启 strict: true
- 除隔离的互操作代码外,不用 any
- 不用 as 断言隐藏类型错误
- 复杂状态用 discriminated unions
- 用 schema 校验运行时 JSON
- 不要没有意义地创建多个相同类型

状态类型示例:
type LoadState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; message: string };

---

# 7. React 状态和副作用

状态放在它真正属于的地方:

| 状态类型 | 放哪 |
| 本地 UI | useState, useReducer |
| URL 状态 | route/search 参数 |
| 服务端状态 | 服务端渲染或 cache/query 层 |
| 表单状态 | 表单 hook/库 |
| 全局 UI | 必要时的小 store |
| 领域状态 | 跨多个流程共享时放 entity/store |

不要把这些放全局 store:
- hover 状态
- 单个下拉的状态
- 单个输入的草稿值
- 单个模态的状态
- 一个组件的临时选中 tab

用 useEffect 跟外部系统同步:
- 浏览器 API
- 定时器
- 订阅
- 外部 store
- DOM 集成

不要用 useEffect 做派生值。

坏:
const [fullName, setFullName] = useState("");
useEffect(() => { setFullName(`${firstName} ${lastName}`); }, [firstName, lastName]);

好:
const fullName = `${firstName} ${lastName}`;

---

# 8. Next.js 边界

App Router 里,组件默认是 Server Components。

只在需要时加 "use client":
- 事件处理器
- 本地状态
- 副作用
- window、document、localStorage
- 拖拽
- contenteditable
- 仅客户端库

不要没有明确需要就把整个 layout 变成 Client Component。

Server-only 代码包括:
- 数据库访问
- 认证
- 私有 API 客户端
- 秘密环境变量
- webhooks
- 访问检查

永远不要把 server-only 模块导入 Client Component。

---

# 9. 运行时校验和数据迁移

在边界校验所有外部数据:
- 请求体
- 表单数据
- URL/search 参数
- 上传文件
- 导入的 JSON
- localStorage/IndexedDB 数据
- 外部 API 响应

加新模型字段时,更新整个生命周期:
1. TypeScript 类型
2. 运行时 schema
3. 工厂/默认值
4. 遗留数据的 parser/migration
5. 归一化 helpers
6. 导入/导出
7. 如果字段可搜索,加搜索/筛选索引
8. 如果用户能编辑,加 undo/redo 快照
9. 创建、编辑、清除该字段的 UI
10. 边界情况和检查

不要在 UI 里加一个字段就完事。

---

# 10. 表单

每个表单必须包含:
- 校验 schema
- 字段错误
- 提交/加载状态
- 提交时禁用提交按钮
- 防止重复提交
- 错误状态
- 成功行为
- 重置/草稿行为(如适用)

只在请求完美成功时能用的表单,不算完成。

---

# 11. shadcn/ui 和共享 UI

默认用 shadcn/ui 快速构建干净一致的界面。

规则:
- 先检查 shadcn registry 里有没有需要的组件
- 通过 CLI 或项目既定方式加 shadcn 组件
- shadcn 已覆盖的场景,不要自建 Button、Input、Modal、Dropdown、Tooltip、Tabs、Card
- 通过 className、variants、组合来适配 shadcn 组件,不要复制相似组件
- 业务组件和原语分开
- components/ui 或 shared/ui 只放 shadcn 原语和薄包装
- 不要把产品特定业务组件放那里
- shadcn 没有的组件,创建跟当前 shadcn 配置一致的最小本地包装

生产力界面的基础 shadcn 组件:
button, input, select, textarea, checkbox, switch, dialog, sheet, dropdown-menu, popover, tooltip, tabs, card, badge, avatar, separator, scroll-area, skeleton, carousel, accordion, collapsible, hover-card

始终用 cn():
export function cn(...values: Array<string | false | null | undefined>) {
  return values.filter(Boolean).join(" ");
}

---

# 12. 选对 UI 表面

加新工具前,选对表面:

| 功能大小 | 放哪 | 例子 |
| 1-5 个快速设置 | 右键菜单/下拉/popover | 状态、截止日期、标签 |
| 5-12 个分组设置 | 分区、可滚动 popover | 实体属性、紧凑筛选 |
| 大数据集或批量操作 | 侧边栏/抽屉 | 筛选、工具面板 |
| 复杂表单或危险操作 | 模态 | 导入/导出、删除确认 |
| 永久工作区 | 专用视图/页面/widget | 仪表盘、日历、编辑器 |

规则:
- 偶尔用的控件放菜单里
- 一直用的控件留在主表面
- 复杂冗长的控件移到侧边栏或模态

不要把小群控件变成页面上的大卡片。

---

# 13. 编辑器/仪表盘/工作区的紧凑 UI

生产力应用里,主内容必须保持焦点。

要求:
- 标题、正文、看板、编辑器不能被次级控件往下推
- 实体属性一般从标题旁的图标按钮打开
- 设置按钮必须有 aria-label
- 重要状态可以显示为小徽章
- 创建/添加操作必须出现在清晰上下文里
- 侧边栏重的流程必须有移动端友好菜单
- 不要把生产力工具做成落地页

坏:
<LargePropertiesCard>
  <Select>Status</Select>
  <Select>Task</Select>
  <Input>Date</Input>
  <Input>Tags</Input>
</LargePropertiesCard>

好:
<TitleRow>
  <TitleInput />
  <PropertiesMenu />
</TitleRow>

---

# 14. Overlay、Dropdown、Popover、右键菜单

每个菜单必须作为真正的 overlay。

规则:
- 菜单可能超出容器时,通过 createPortal(..., document.body) 渲染
- 用 position: fixed 或可靠定位 helper
- 设明确 z-index
- 用不透明 backgroundColor
- 不要只靠半透明 bg-black/50 或模糊
- 加边框、ring 或阴影
- 设 max-height 和 overflow-y-auto
- Escape 关闭
- 外部点击/触摸关闭
- 防止页面文字透出或盖在菜单上
- hover 和 active 状态不能改变项尺寸

最小 overlay 样式:
<div
  role="menu"
  className="rounded-2xl border p-2 shadow-2xl"
  style={{ backgroundColor: "#151a21", boxShadow: "0 24px 70px rgb(0 0 0 / 78%)", isolation: "isolate", zIndex: 1000 }}
>
  ...
</div>

---

# 15. 菜单里的选项列表

菜单里的任务、项目、用户、标签列表,不能像一堵密不透风的文字墙。

两行项:
- min-height 40-44px
- icon、text、checkmark 之间留 gap
- 用 py-1.5 这样的纵向内边距
- 标题和 metadata 不同行高
- 标题和 metadata 之间加 mt-0.5
- 文字父级加 min-w-0
- 标题和 metadata 加 truncate
- checkmark 和 icon 加 shrink-0

---

# 16. 长文本和溢出

任何用户提供的文本都可能含无空格长单词。

编辑器、contenteditable、Markdown、卡片标题、评论:
- flex/grid 子元素用 min-w-0
- 用当前 Tailwind 工具类换行长单词
- 新 Tailwind 版本里 break-words 可能写成 wrap-break-word
- 用换行、溢出、text-wrap、grid、spacing 类前先查项目 Tailwind 版本文档
- 元素在 flex 容器里且长文本破坏宽度时,检查是否该用 wrap-anywhere
- 卡片短行用 truncate
- 正文换行,不要横向溢出
- 文字不能渲染到菜单、popover、模态上
- 用无空格长字符串测试

---

# 17. Tailwind CSS:核对当前类名

AI 用新类或版本相关类前,必须查项目装的 Tailwind 版本。

流程:
1. 看 package.json 和 lockfile
2. 确定 Tailwind 主版本
3. 类可能跨版本不同时,查该确切版本的官方文档
4. 不要没验证就机械替换类
5. 用任意值时,确认它进了构建产物

特别注意:break-words / wrap-break-word / wrap-anywhere;text-wrap、text-balance、text-pretty;overflow-*;size-*;任意颜色如 bg-[#151a21];任意阴影;任意 grid 模板;动态类名。

不要这样构造动态 Tailwind 类:
const color = "red";
return <div className={`bg-${color}-500`} />;

用 map:
const colorClassName = { danger: "bg-red-500", success: "bg-emerald-500" };

---

# 18. 布局和侧边栏折叠

折叠侧边栏或抽屉不能改变页面高度或留下空块。

规则:
- app shell: h-dvh min-h-dvh overflow-hidden
- 内部区域: flex min-h-0 flex-1 overflow-hidden
- 只在合适区域用 overflow-y-auto 开滚动
- 折叠时改宽度/flex-basis,不改高度
- 折叠的侧边栏要有稳定宽度
- 提供清晰的恢复控制
- 破坏性或创建操作不能是没上下文的孤立按钮
- 偏好可以持久化到 localStorage

---

# 19. 浏览器 API 和 localStorage

Next.js 里,浏览器 API 只在 Client Components 可用。

规则:
- 用 localStorage、window、document、拖拽、contenteditable 的文件必须加 "use client"
- 不要在 Server Component 读 localStorage
- 不要因初始值不同导致 hydration 错误
- storage 操作用 try/catch 包
- storage 失败不能弄坏 UI
- 刷新后验证持久化 UI 偏好
- 构建不能因 window is not defined 失败

---

# 20. 工具间关系

如果一个实体链接到另一个,关系必须是真的:
- 存进 model
- 在 UI 展示
- 点击打开链接实体
- 创建相关实体时立即保存关系
- 导入/导出时保留关系
- 有用时把关系纳入搜索/筛选
- 相关实体删除时,UI 显示 fallback

关系没持久化,就不要做装饰性的"链接"按钮。

---

# 21. 统一领域操作

每个用户操作必须有单一事实来源。

不要:
- 从斜杠菜单用一种方式创建实体
- 从工具栏用另一种方式创建
- 从命令面板绕过校验
- 在右键菜单重复 mutation 逻辑

要:
- 领域操作放一处
- UI 组件调用那个操作
- 每个入口用同样的校验和约束

---

# 23. 无障碍

要求:
- 操作用 <button>
- 导航用 <a>
- 纯图标按钮加 aria-label
- 输入提供 label
- 显示可见焦点状态
- 支持键盘导航
- 模态和 popover 用 Escape 关闭
- 外部点击关闭 popover
- 模态里用 focus trap
- 不要只用颜色传达含义
- 不要用 <div> 替代 <button>

---

# 24. 加载、空、错误状态

数据驱动屏幕必须考虑:
- 加载
- 成功
- 空状态
- 权限拒绝
- 网络错误
- 服务端错误
- 重试

没有任何解释的空白屏是 bug。

---

# 25. 安全

要求:
- 秘密只在服务端
- 用运行时校验
- 访问控制在服务端强制
- 校验文件 MIME 类型和大小
- 净化用户提供的 HTML
- 不用 sanitizer 就不要用 dangerouslySetInnerHTML
- 不记录 token 或个人数据
- 不信任浏览器提供的 role 或 userId 值

---

# 26. 性能

先测量,再优化。

用:
- 对重的编辑器、图表、地图、PDF 模块做动态导入
- 图片优化
- 大列表虚拟化
- 搜索用 abort/stale-request 保护
- selectors 减少重渲染

不要没理由地加 memoization。

---

# 27. 用真实内容测试设计

额外界面质量参考:https://jakub.kr/skills/make-interfaces-feel-better

完成 UI 任务前,用这些测试:
- 无空格长单词
- 长俄语标题
- 短标题
- 空标题
- 多个标签
- 长列表/分类名
- 下拉里多个选项
- 激活和非激活状态
- 有日期和缺日期

验证:
- 没有重叠
- overlay 盖住底层内容
- 文字不透出菜单
- 徽章不纵向压缩文字
- 元素不挤在一起
- 滚动条不盖重要文字
- hover 和 focus 状态易读
- 桌面和移动宽度都正确

---

# 28. 变更后检查

改完代码跑:
npm run typecheck
npm run lint
npm run build

改了 UI:
- 浏览器打开页面
- 走主用户流程
- 测键盘和鼠标交互
- 测 Escape 和外部点击
- 测刷新
- 测长文本
- 测移动视口宽度
- 视觉层改了截图

浏览器验证不了就明说。不要把 typecheck 当视觉验证。

---

# 29. Git 和工作区

改之前看当前状态:
git status --short

规则:
- 没有明确要求,不要回退别人的改动
- 没有明确许可,不用破坏性命令
- 不做无关重构
- 用户没要求就不自动提交
- 不要不必要地改行尾或重排整个项目

---

# 30. 最终报告

最终回复里说明:
- 改了什么
- 哪些文件重要
- 跑了哪些检查
- 什么没法验证
- 还有什么风险

报告简洁诚实。

评论 0

更多

登录后可点赞、收藏、评论和举报。

还没有评论,先发起一个具体问题。

0/2000