Petrichor 技术可视化 · Frontend Stack Selection

前端 UI 技术选型

一张图看清 Petrichor 前台用了什么、为什么这么选。基座是 Next.js 16 + React 19, 样式走 Tailwind v4 + 无头原语自建(src/components/ui 已 152 个组件), 成品组件库只在垂直场景按需空降。

← 返回文章列表 Agent 架构 ↗
一句话本质:优先「无头原语 + Tailwind 自建」,把样式主权攥在自己手里。

成品组件库(Ant Design、LobeHub)只在富文本、AI 对话这类垂直场景按需空降; 每个重依赖都留退路——拆掉不伤主干。选型来源是仓库里真实的 package.json

TS → Next 16 → Tailwind v4 → 无头原语 → 自建 UI → 领域能力

01 · 分层全景

六层栈:从语言底座到领域能力

点步进器或图里的任意一层聚焦;下方详情会列出该层的真实依赖与选它的理由。上层依赖下层,越往上越重、越该慎选。

上层依赖下层
领域能力层 PlateJS · assistant-ui + AI SDK · Recharts / React Flow 6 业务组件层 shadcn 模式自建 ×152 · Ant Design 6 · LobeHub UI 5 无头交互原语 Radix UI · Base UI · Ariakit · cmdk · vaul 4 样式系统 Tailwind v4 · cva · cn()(clsx + tailwind-merge) 3 运行时框架 Next.js 16 App Router · React 19 · next-themes 2 语言与构建底座 TypeScript 5.9 · Turbopack · Vitest 4 1

提示:悬停某一层看它的一句话定位,点击则展开该层的依赖清单与选型理由。

键盘 ← → 切层 · P 导览 · Esc 退出聚焦

02 · 选型原则

四条不成文的规矩

这些原则不写在代码里,但决定了上面每一层为什么长这样。

无头优先

交互逻辑与可访问性交给无头库(Radix / Base UI / Ariakit),样式全权自持。升级不被组件库的视觉锁死,改一处 token 全站跟着变。

单向依赖

底层零业务、上层不反向依赖下层的实现细节。cn() 收口所有 class 合并,主题走 next-themes 一处切换,杜绝散落的样式判断。

一处一职、允许并存

同类库只在边界清晰时并存:Zustand 管全局、Jotai 管原子;dnd-kit 走新功能、react-dnd 留存量。并存是过渡态,不是终态。

可回退

每个重依赖都留退路。成品组件库只占各自的垂直页面,拆掉不伤主干;越靠上层越贵的依赖,越要能被换掉。

03 · 关键抉择

六个岔路口,选了什么、放弃了什么

每一条都有真实备选。列出来是为了让「为什么不是那个」也留痕。

富文本编辑器

选择PlateJS 52(基于 Slate)
备选TipTap / Lexical
理由40+ 官方插件、docx 与 markdown 双向、评论 / 建议 / 协同齐活;生态深度压过迁移成本。

样式方案

选择Tailwind v4 + shadcn 模式
备选CSS-in-JS / 组件库全家桶
理由原子类零运行时,cva 管变体,组件源码在自己仓库;改样式不排队等上游。

状态管理

选择Zustand + Jotai
备选Redux Toolkit / 单一大 store
理由全局用 Zustand store、局部用 Jotai 原子,零样板;未引入 Redux。

AI 对话 UI

选择assistant-ui + Vercel AI SDK
备选全自建对话层
理由流式、工具卡、Markdown 渲染开箱即用;后端接 Mastra 运行时,前端不重造轮子。

数据获取

选择RSC + Server Actions + axios
备选TanStack Query
理由服务端组件直取数据,客户端只留必要请求;未引入 React Query,减一层缓存心智。

表格与长列表

选择TanStack Table + Virtual
备选AntD Table 独大
理由无头表格配自建样式,万行列表交给 react-virtual;AntD 只在重后台页兜底。

04 · 完整选型清单

14 个域,一屏看全前端依赖

逐条取自 apps/web/package.json。颜色标出每个依赖在选型里的角色。

前端依赖面板

核心主干撑起日常;成品库只在垂直场景空降;无头 / 自建 / 构建类是骨架;虚线红 是存量或可替换项。

框架 & 底座framework
Next.js 16 React 19 TypeScript 5.9 Turbopack Vite 7 Vitest 4 ESLint 9 biome check
样式系统styling
Tailwind v4 cva clsx tailwind-merge next-themes tw-animate-css scrollbar-hide sonner
无头原语headless
radix-ui @base-ui/react @ariakit/react cmdk vaul input-otp
成品组件库components
shadcn 自建 ×152 antd 6 @lobehub/ui
富文本编辑器editor
platejs 52 @platejs/* 40+ @udecode/cn @excalidraw/excalidraw
AI & 对话ai
ai · Vercel AI SDK @ai-sdk/openai @assistant-ui/react @mastra/core mcp-handler
状态 & 数据data
zustand jotai zod 4 axios drizzle-orm better-auth @supabase/ssr @upstash/redis
表格 & 虚拟化table
@tanstack/react-table @tanstack/react-virtual
可视化 & 画布viz
recharts @xyflow/react mind-elixir heat-graph @dayflow/core
动画motion
motion gsap @rive-app/react border-beam tw-shimmer
拖拽dnd
@dnd-kit/core @dnd-kit/sortable react-dnd
图标 & Emojiicons
lucide-react @tabler/icons-react @hugeicons/react @lobehub/icons @emoji-mart/data
文档 & 文件files
pdfjs-dist @embedpdf/* xlsx docx-preview uploadthing @zumer/snapdom
Markdown & 高亮markdown
react-markdown marked remark-gfm rehype-sanitize shiki lowlight
主干核心主干 按需成品库 · 按需空降 自建无头 / 自建 / 构建 备选存量 · 可替换

选型边界(known tensions)

  • 三套组件观并存有重量:自建 152 组件 + AntD 6 + LobeHub UI,包体与视觉一致性靠「各守垂直页」的纪律来克制。
  • 图标库 5 套按来源就近取用,未强行收敛;靠 optimizePackageImports 与 tree-shaking 兜住体积。
  • 拖拽 dnd-kit 与 react-dnd 并存:新功能一律 dnd-kit,react-dnd 是待迁移存量。
  • 富文本 40+ 依赖是最大单点,换栈成本极高——已用分包与按需导入缓解首屏。
  • 未引入独立表单库(RHF / Formik):轻表单手写 + Zod 4 校验,复杂表单场景需再评估。