mcpField
S
shadcn-inertia

shadcn-inertia

github.com/inertia-rails开发70433 次安装37 周前更新
代码仓库

关于 shadcn-inertia

shadcn-inertia 是一项技能,用于将 shadcn/ui 组件适配到 Inertia.js、Rails 和 React 项目。它说明如何以 Inertia 模式替换 Next.js 和 react-hook-form 约定,涵盖表单、导航、闪消息提示及主题处理。

主要功能

  1. 01将 Next.js 模式映射为 Inertia 用法
  2. 02Inertia 表单字段指南
  3. 03单页导航与 URL 对话框
  4. 04闪消息提示接线方式
  5. 05不使用 next-themes 的暗色模式设置

适用场景

  1. 01将 shadcn 表单适配到 Inertia
  2. 02为服务端表格添加排序
  3. 03将闪消息接入 Sonner 提示
  4. 04避免暗色主题切换时闪屏

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/inertia-rails/skills --skill shadcn-inertia

安装后在 Claude Code 中输入/shadcn-inertia确认已加载。

附带文件references/components.mdreferences/flash-toast.md

详细介绍

Inertia Rails 的 shadcn/ui

为 Inertia.js + Rails + React 适配的 shadcn/ui 模式。不是 Next.js。

使用 shadcn 示例前,请先确认:

  • 它使用了 react-hook-form + zod 吗? → 替换为 Inertia <Form> + name 属性。Inertia 会处理 CSRF、错误、重定向和处理中状态,而 react-hook-form 会与这些功能冲突。
  • 它使用了 'use client' 吗? → 移除它。Inertia 没有 RSC——所有组件都是客户端组件。
  • 它使用了 next/link、next/head、useRouter() 吗? → 替换为 Inertia <Link>、<Head>、router。

与 Next.js 默认配置的主要区别

shadcn 默认配置(Next.js)Inertia 对应项
'use client' 指令移除——不需要(没有 RSC)
react-hook-form + zodInertia <Form> 组件
FormField、FormItem、FormMessage普通 <Input name="..."> + errors.field
next-themesCSS 类策略 + @custom-variant
useRouter()(Next)来自 @inertiajs/react 的 router
next/link来自 @inertiajs/react 的 <Link>
next/head来自 @inertiajs/react 的 <Head>

绝不要使用 shadcn 的 FormField、FormItem、FormLabel、FormMessage 组件—— 它们内部依赖 react-hook-form 的 useFormContext,没有它就会崩溃。 请在 Inertia <Form> 内使用带 name 属性的普通 shadcn Input/Label/Select, 并通过渲染函数的 errors 对象显示错误(见下方示例)。

设置

运行 npx shadcn@latest init。如果 tsconfig.json 中没有 @/ 解析别名,请添加, 不要在 vite.config.ts 中添加 @/ 解析别名——vite-plugin-ruby 已经提供了这些别名。

在 Inertia <Form> 中使用 shadcn 输入组件

在 Inertia <Form> 内使用带 name 属性的普通 shadcn Input/Label/Button。 完整的 <Form> API 请参阅 inertia-rails-forms 技能——本节仅介绍针对 shadcn 的适配。

关键模式: 将 shadcn 的 FormField/FormItem/FormMessage 替换为普通组件

  • 手动显示错误:
// shadcn error display pattern (replaces FormMessage):
<Label htmlFor="name">Name</Label>
<Input id="name" name="name" />
{errors.name && <p className="text-sm text-destructive">{errors.name}</p>}

Inertia <Form> 集成要求 <Select> 必须带有 name 属性——shadcn 示例中省略了它, 因为 react-hook-form 管理值的方式不同:

<Select name="role" defaultValue="member">
  <SelectTrigger><SelectValue placeholder="Select role" /></SelectTrigger>
  <SelectContent>
    <SelectItem value="admin">Admin</SelectItem>
    <SelectItem value="member">Member</SelectItem>
  </SelectContent>
</Select>

使用 Inertia 导航的对话框

import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { router } from '@inertiajs/react'

function UserDialog({ open, user }: { open: boolean; user: User }) {
  return (
    <Dialog
      open={open}
      onOpenChange={(isOpen) => {
        if (!isOpen) {
          router.replaceProp('show_dialog', false)
        }
      }}
    >
      <DialogContent>
        <DialogHeader>
          <DialogTitle>{user.name}</DialogTitle>
        </DialogHeader>
        {/* content */}
      </DialogContent>
    </Dialog>
  )
}

使用服务器端排序的表格

shadcn <Table> 可正常渲染。Inertia 特有的部分是通过 router.get 排序:

const handleSort = (column: string) => {
  router.get('/users', { sort: column }, { preserveState: true })
}

<TableHead onClick={() => handleSort('name')} className="cursor-pointer">
  Name {sort === 'name' && '↑'}
</TableHead>

行链接请使用 <Link>(而不是 <a>),以保留 SPA 导航。

使用 Flash 消息显示 Toast

Flash 配置(flash_keys)位于 inertia-rails-controllers。Flash 访问(usePage().flash) 位于 inertia-rails-pages。本节仅介绍 Toast UI 的接线配置。

必须——实现基于 flash 的 Sonner Toast 时,请阅读整个文件: references/flash-toast.md(约 80 行)——包含完整的 useFlash hook 和 Sonner Toast provider。如果只是读取 flash 值,不涉及 Toast UI,则不要加载。

注意:Rails 初始化程序中的 flash_keys 必须与 TypeScript 类型 FlashData 匹配——除非同时更新两者,否则不要使用 success/error。

深色模式(不使用 next-themes)

npx shadcn@latest init 会生成浅色/深色模式的 CSS 变量,并在 CSS(Tailwind v4)中添加 @custom-variant dark (&:is(.dark *));。这些变量本身无需额外设置。

关键——防止错误主题闪烁(FOUC): Next.js 会自动处理此问题;Inertia 不会。 请在 <head> 中添加内联脚本(在 React 水合之前),并在 Inertia 入口文件中调用 initializeTheme():

<%# app/views/layouts/application.html.erb — in <head>, before any stylesheets %>
<script>
  document.documentElement.classList.toggle(
    "dark",
    localStorage.appearance === "dark" ||
      (!("appearance" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches),
  );
</script>
// app/frontend/entrypoints/inertia.tsx
import { initializeTheme } from '@/hooks/use-appearance'
initializeTheme() // must run before createInertiaApp

请使用 useAppearance hook(支持浅色/深色/系统模式、localStorage 持久化、 matchMedia 监听器),而不是 next-themes。通过 <html> 上的 .dark 类进行切换——无需 provider。

故障排查

现象原因修复方法
FormField/FormMessage 崩溃使用了依赖 react-hook-form 的 shadcn 表单组件替换为普通的 Input/Label,并显示 errors.field
Select 值未提交缺少 name 属性为 <Select> 添加 name="field" — shadcn 示例中省略了它
对话框意外关闭缺少或错误的 onOpenChange 处理器使用 onOpenChange={(open) => { if (!open) closeHandler() }}
错误主题闪烁(FOUC)<head> 中缺少内联 <script>在样式表之前添加深色模式脚本(参见“深色模式”部分)

相关技能

  • 表单组件 → inertia-rails-forms(<Form> 渲染函数、useForm)
  • Flash 配置 → inertia-rails-controllers(flash_keys 初始化器)
  • Flash 访问 → inertia-rails-pages(usePage().flash)
  • 由 URL 驱动的对话框 → inertia-rails-pages(router.get 模式)

参考资料

构建上述示例未涵盖的 shadcn 组件(Accordion、Sheet、Tabs、DropdownMenu、 带有 Inertia 模式的 AlertDialog)时,加载 references/components.md(约 300 行)。

不要加载 components.md 来了解基本的 Form、Select、Dialog 或 Table 用法 — 上述示例已足够。

评论

支持 Markdown