shadcn-inertia
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseshadcn/ui for Inertia Rails
适用于Inertia Rails的shadcn/ui
shadcn/ui patterns adapted for Inertia.js + Rails + React. NOT Next.js.
Before using a shadcn example, ask:
- Does it use +
react-hook-form? → Replace with Inertiazod+<Form>attributes. Inertia handles CSRF, errors, redirects, processing state — react-hook-form would fight all of this.name - Does it use ? → Remove it. Inertia has no RSC — all components are client components.
'use client' - Does it use ,
next/link,next/head? → Replace with InertiauseRouter(),<Link>,<Head>.router
为Inertia.js + Rails + React适配的shadcn/ui模式。非Next.js。
使用shadcn示例前,请确认:
- 是否使用了+
react-hook-form? → 替换为Inertiazod+<Form>属性。Inertia会处理CSRF、错误、重定向、处理状态——react-hook-form会与这些机制冲突。name - 是否使用了? → 移除该指令。Inertia不存在RSC——所有组件都是客户端组件。
'use client' - 是否使用了、
next/link、next/head? → 替换为Inertia的useRouter()、<Link>、<Head>。router
Key Differences from Next.js Defaults
与Next.js默认配置的主要差异
| shadcn default (Next.js) | Inertia equivalent |
|---|---|
| Remove — not needed (no RSC) |
| Inertia |
| Plain |
| CSS class strategy + |
| |
| |
| |
NEVER use shadcn's , , , components —
they depend on react-hook-form's internally and will crash without it.
Use plain shadcn // with attributes inside Inertia ,
and render errors from the render function's object (see examples below).
FormFieldFormItemFormLabelFormMessageuseFormContextInputLabelSelectname<Form>errors| shadcn默认配置(Next.js) | Inertia对应方案 |
|---|---|
| 移除——无需使用(无RSC) |
| Inertia |
| 普通 |
| CSS类策略 + |
| 来自 |
| 来自 |
| 来自 |
切勿使用shadcn的、、、组件——
它们内部依赖react-hook-form的,没有该环境会导致崩溃。
请在Inertia 内部使用普通的shadcn //组件并添加属性,
通过渲染函数的对象展示错误(见下方示例)。
FormFieldFormItemFormLabelFormMessageuseFormContext<Form>InputLabelSelectnameerrorsSetup
配置步骤
npx shadcn@latest init@/tsconfig.json@/vite.config.tsvite-plugin-ruby执行。如果中没有解析别名,请添加,
请勿在中添加解析别名——已自动提供该配置。
npx shadcn@latest inittsconfig.json@/vite.config.ts@/vite-plugin-rubyshadcn Inputs in Inertia <Form>
<Form>在Inertia <Form>
中使用shadcn输入组件
<Form>Use plain shadcn // with attributes inside Inertia .
See skill for full API — this section covers shadcn-specific adaptation only.
InputLabelButtonname<Form>inertia-rails-forms<Form>The key pattern: Replace shadcn's // with plain
components + manual error display:
FormFieldFormItemFormMessagetsx
// 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>}<Select>name<Form>tsx
<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 内部使用普通的shadcn //组件并添加属性。
完整的 API请参考技能——本节仅介绍shadcn相关的适配内容。
<Form>InputLabelButtonname<Form>inertia-rails-forms核心模式: 用普通组件+手动错误展示替代shadcn的//:
FormFieldFormItemFormMessagetsx
// 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>}<Select>name<Form>tsx
<Select name="role" defaultValue="member">
<SelectTrigger><SelectValue placeholder="Select role" /></SelectTrigger>
<SelectContent>
<SelectItem value="admin">Admin</SelectItem>
<SelectItem value="member">Member</SelectItem>
</SelectContent>
</Select>Dialog with Inertia Navigation
结合Inertia导航的对话框
tsx
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>
)
}tsx
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>
)
}Table with Server-Side Sorting
服务端排序的表格
shadcn renders normally. The Inertia-specific part is sorting via :
<Table>router.gettsx
const handleSort = (column: string) => {
router.get('/users', { sort: column }, { preserveState: true })
}
<TableHead onClick={() => handleSort('name')} className="cursor-pointer">
Name {sort === 'name' && '↑'}
</TableHead>Use (not ) for row links to preserve SPA navigation.
<Link><a>shadcn 可正常渲染。Inertia相关的部分是通过实现排序:
<Table>router.gettsx
const handleSort = (column: string) => {
router.get('/users', { sort: column }, { preserveState: true })
}
<TableHead onClick={() => handleSort('name')} className="cursor-pointer">
Name {sort === 'name' && '↑'}
</TableHead>请使用(而非)作为行链接,以保留SPA导航体验。
<Link><a>Toast with Flash Messages
结合Flash消息的提示框
Flash config () is in . Flash access
() is in . This section covers toast UI wiring only.
flash_keysinertia-rails-controllersusePage().flashinertia-rails-pagesMANDATORY — READ ENTIRE FILE when implementing flash-based toasts with Sonner:
(~80 lines) — full
hook and Sonner toast provider. Do NOT load if only reading flash values without toast UI.
references/flash-toast.mduseFlashKey gotcha: in the Rails initializer MUST match your
TypeScript type — do NOT use / unless you also update both.
flash_keysFlashDatasuccesserrorFlash配置()在中。Flash访问
()在中。本节仅介绍提示框UI的关联配置。
flash_keysinertia-rails-controllersusePage().flashinertia-rails-pages必须操作——实现基于Flash的Sonner提示框时请完整阅读文件:
(约80行)——包含完整的
钩子和Sonner提示框提供者。如果仅读取Flash值而不使用提示框UI,请不要加载该文件。
references/flash-toast.mduseFlash关键注意事项:Rails初始化器中的必须与你的
TypeScript类型匹配——除非同时更新两者,否则不要使用/。
flash_keysFlashDatasuccesserrorDark Mode (No next-themes)
深色模式(无需next-themes)
npx shadcn@latest init@custom-variant dark (&:is(.dark *));CRITICAL — prevent flash of wrong theme (FOUC): Next.js handles this
automatically; Inertia does NOT. Add an inline script in (before React
hydrates) and call in your Inertia entrypoint:
<head>initializeTheme()erb
<%# 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>tsx
// app/frontend/entrypoints/inertia.tsx
import { initializeTheme } from '@/hooks/use-appearance'
initializeTheme() // must run before createInertiaAppUse a hook (light/dark/system modes, localStorage persistence,
listener) instead of . Toggle via class on
— no provider needed.
useAppearancematchMedianext-themes.dark<html>npx shadcn@latest init@custom-variant dark (&:is(.dark *));重要——避免主题闪烁(FOUC):Next.js会自动处理该问题;但Inertia不会。请在中添加内联脚本(在React水合之前),并在Inertia入口文件中调用:
<head>initializeTheme()erb
<%# app/views/layouts/application.html.erb — 在<head>中,所有样式表之前 %>
<script>
document.documentElement.classList.toggle(
"dark",
localStorage.appearance === "dark" ||
(!("appearance" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches),
);
</script>tsx
// app/frontend/entrypoints/inertia.tsx
import { initializeTheme } from '@/hooks/use-appearance'
initializeTheme() // 必须在createInertiaApp之前调用请使用钩子(支持浅色/深色/系统模式、本地存储持久化、
监听)替代。通过元素的类切换主题——无需提供者。
useAppearancematchMedianext-themes<html>.darkTroubleshooting
故障排查
| Symptom | Cause | Fix |
|---|---|---|
| Using shadcn form components that depend on react-hook-form | Replace with plain |
| Missing | Add |
| Dialog closes unexpectedly | Missing or wrong | Use |
| Flash of wrong theme (FOUC) | Missing inline | Add dark mode script before stylesheets (see Dark Mode section) |
| 症状 | 原因 | 修复方案 |
|---|---|---|
| 使用了依赖react-hook-form的shadcn表单组件 | 替换为普通 |
| 缺少 | 为 |
| 对话框意外关闭 | | 使用 |
| 主题闪烁(FOUC) | | 在样式表之前添加深色模式脚本(见深色模式章节) |
Related Skills
相关技能
- Form component → (
inertia-rails-formsrender function, useForm)<Form> - Flash config → (flash_keys initializer)
inertia-rails-controllers - Flash access → (usePage().flash)
inertia-rails-pages - URL-driven dialogs → (router.get pattern)
inertia-rails-pages
- 表单组件 → (
inertia-rails-forms渲染函数、useForm)<Form> - Flash配置 → (flash_keys初始化器)
inertia-rails-controllers - Flash访问 → (usePage().flash)
inertia-rails-pages - URL驱动的对话框 → (router.get模式)
inertia-rails-pages
References
参考资料
Load (~300 lines) when building
shadcn components beyond those shown above (Accordion, Sheet, Tabs, DropdownMenu,
AlertDialog with Inertia patterns).
references/components.mdDo NOT load for basic Form, Select, Dialog, or Table usage —
the examples above are sufficient.
components.md当构建上述示例之外的shadcn组件(如Accordion、Sheet、Tabs、DropdownMenu、
结合Inertia模式的AlertDialog)时,请加载(约300行)。
references/components.md如果仅使用基础的Form、Select、Dialog或Table,请不要加载——
上述示例已足够使用。
components.md