Loading...
Loading...
Compare original and translation side by side
react-hook-formzod<Form>name'use client'next/linknext/headuseRouter()<Link><Head>routerreact-hook-formzod<Form>name'use client'next/linknext/headuseRouter()<Link><Head>router| shadcn default (Next.js) | Inertia equivalent |
|---|---|
| Remove — not needed (no RSC) |
| Inertia |
| Plain |
| CSS class strategy + |
| |
| |
| |
FormFieldFormItemFormLabelFormMessageuseFormContextInputLabelSelectname<Form>errors| shadcn默认配置(Next.js) | Inertia对应方案 |
|---|---|
| 移除——无需使用(无RSC) |
| Inertia |
| 普通 |
| CSS类策略 + |
| 来自 |
| 来自 |
| 来自 |
FormFieldFormItemFormLabelFormMessageuseFormContext<Form>InputLabelSelectnameerrorsnpx shadcn@latest init@/tsconfig.json@/vite.config.tsvite-plugin-rubynpx shadcn@latest inittsconfig.json@/vite.config.ts@/vite-plugin-ruby<Form><Form>InputLabelButtonname<Form>inertia-rails-forms<Form>FormFieldFormItemFormMessage// 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><Select name="role" defaultValue="member">
<SelectTrigger><SelectValue placeholder="Select role" /></SelectTrigger>
<SelectContent>
<SelectItem value="admin">Admin</SelectItem>
<SelectItem value="member">Member</SelectItem>
</SelectContent>
</Select><Form>InputLabelButtonname<Form>inertia-rails-formsFormFieldFormItemFormMessage// 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><Select name="role" defaultValue="member">
<SelectTrigger><SelectValue placeholder="Select role" /></SelectTrigger>
<SelectContent>
<SelectItem value="admin">Admin</SelectItem>
<SelectItem value="member">Member</SelectItem>
</SelectContent>
</Select>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>
)
}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>router.getconst handleSort = (column: string) => {
router.get('/users', { sort: column }, { preserveState: true })
}
<TableHead onClick={() => handleSort('name')} className="cursor-pointer">
Name {sort === 'name' && '↑'}
</TableHead><Link><a><Table>router.getconst handleSort = (column: string) => {
router.get('/users', { sort: column }, { preserveState: true })
}
<TableHead onClick={() => handleSort('name')} className="cursor-pointer">
Name {sort === 'name' && '↑'}
</TableHead><Link><a>flash_keysinertia-rails-controllersusePage().flashinertia-rails-pagesreferences/flash-toast.mduseFlashflash_keysFlashDatasuccesserrorflash_keysinertia-rails-controllersusePage().flashinertia-rails-pagesreferences/flash-toast.mduseFlashflash_keysFlashDatasuccesserrornpx shadcn@latest init@custom-variant dark (&:is(.dark *));<head>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 createInertiaAppuseAppearancematchMedianext-themes.dark<html>npx shadcn@latest init@custom-variant dark (&:is(.dark *));<head>initializeTheme()<%# 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>// app/frontend/entrypoints/inertia.tsx
import { initializeTheme } from '@/hooks/use-appearance'
initializeTheme() // 必须在createInertiaApp之前调用useAppearancematchMedianext-themes<html>.dark| 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) | | 在样式表之前添加深色模式脚本(见深色模式章节) |
inertia-rails-forms<Form>inertia-rails-controllersinertia-rails-pagesinertia-rails-pagesinertia-rails-forms<Form>inertia-rails-controllersinertia-rails-pagesinertia-rails-pagesreferences/components.mdcomponents.mdreferences/components.mdcomponents.md