一份给 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
更多
登录后可点赞、收藏、评论和举报。
还没有评论,先发起一个具体问题。