shadcn-svelte-inertia
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseshadcn-svelte for Inertia Rails
适用于Inertia Rails的shadcn-svelte
shadcn-svelte (bits-ui) patterns adapted for Inertia.js + Rails + Svelte. NOT SvelteKit.
Before using a shadcn-svelte example, ask:
- Does it use SvelteKit-specific APIs? (,
goto,$app/navigationfunctions,load) → Replace with Inertia+page.svelte, server props, page componentsrouter - Does it use +
sveltekit-superforms? → Replace with Inertiazod+<Form>attributes. Inertia handles CSRF, errors, redirects, processing state.name
为Inertia.js + Rails + Svelte适配的shadcn-svelte(bits-ui)模式。注意:不适用于SvelteKit。
在使用shadcn-svelte示例前,请确认:
- 是否使用了SvelteKit专属API?(、
goto、$app/navigation函数、load)→ 替换为Inertia的+page.svelte、服务器props、页面组件router - 是否使用了+
sveltekit-superforms? → 替换为Inertia的zod+<Form>属性。Inertia会处理CSRF、错误、重定向和处理状态。name
Key Differences from SvelteKit Defaults
与SvelteKit默认配置的主要区别
| shadcn-svelte default (SvelteKit) | Inertia equivalent |
|---|---|
| |
| Server-rendered props via Rails controller |
| Default exports with module script layout |
| Inertia |
| |
| shadcn-svelte默认配置(SvelteKit) | Inertia等效方案 |
|---|---|
来自 | 来自 |
| 通过Rails控制器渲染的服务器端props |
| 带模块脚本布局的默认导出 |
| Inertia的 |
| |
Setup
设置步骤
npx shadcn-svelte@latest init@/tsconfig.json@/vite.config.tsvite-plugin-ruby执行。如果中没有解析别名,请添加。
请勿在中添加解析别名——已提供该功能。
npx shadcn-svelte@latest inittsconfig.json@/vite.config.ts@/vite-plugin-rubyshadcn-svelte Inputs in Inertia <Form>
<Form>在Inertia <Form>
中使用shadcn-svelte输入组件
<Form>Use plain shadcn-svelte // with attributes inside Inertia .
See skill (+ ) for full API.
InputLabelButtonname<Form>inertia-rails-formsreferences/svelte.md<Form>The key pattern: Use to access form state:
{#snippet}svelte
<script lang="ts">
import { Form } from '@inertiajs/svelte'
import { Input } from '$lib/components/ui/input'
import { Label } from '$lib/components/ui/label'
import { Button } from '$lib/components/ui/button'
</script>
<Form method="post" action="/users">
{#snippet children({ errors, processing })}
<div class="space-y-4">
<div>
<Label for="name">Name</Label>
<Input id="name" name="name" />
{#if errors.name}<p class="text-sm text-destructive">{errors.name}</p>{/if}
</div>
<div>
<Label for="email">Email</Label>
<Input id="email" name="email" type="email" />
{#if errors.email}<p class="text-sm text-destructive">{errors.email}</p>{/if}
</div>
<Button type="submit" disabled={processing}>
{processing ? 'Creating...' : 'Create User'}
</Button>
</div>
{/snippet}
</Form>Svelte 4: instead of .
<Form let:errors let:processing>{#snippet}<Select>name<Form>svelte
<Select name="role" value="member">
<SelectTrigger><SelectValue placeholder="Select role" /></SelectTrigger>
<SelectContent>
<SelectItem value="admin">Admin</SelectItem>
<SelectItem value="member">Member</SelectItem>
</SelectContent>
</Select>在Inertia 内部,使用普通的shadcn-svelte //并添加属性。
如需完整的 API,请参考技能(以及)。
<Form>InputLabelButtonname<Form>inertia-rails-formsreferences/svelte.md核心模式: 使用语法访问表单状态:
{#snippet}svelte
<script lang="ts">
import { Form } from '@inertiajs/svelte'
import { Input } from '$lib/components/ui/input'
import { Label } from '$lib/components/ui/label'
import { Button } from '$lib/components/ui/button'
</script>
<Form method="post" action="/users">
{#snippet children({ errors, processing })}
<div class="space-y-4">
<div>
<Label for="name">姓名</Label>
<Input id="name" name="name" />
{#if errors.name}<p class="text-sm text-destructive">{errors.name}</p>{/if}
</div>
<div>
<Label for="email">邮箱</Label>
<Input id="email" name="email" type="email" />
{#if errors.email}<p class="text-sm text-destructive">{errors.email}</p>{/if}
</div>
<Button type="submit" disabled={processing}>
{processing ? '创建中...' : '创建用户'}
</Button>
</div>
{/snippet}
</Form>Svelte 4版本:使用替代。
<Form let:errors let:processing>{#snippet}<Select>name<Form>svelte
<Select name="role" value="member">
<SelectTrigger><SelectValue placeholder="选择角色" /></SelectTrigger>
<SelectContent>
<SelectItem value="admin">管理员</SelectItem>
<SelectItem value="member">普通成员</SelectItem>
</SelectContent>
</Select>Dialog with Inertia Navigation
结合Inertia导航的对话框
svelte
<script lang="ts">
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '$lib/components/ui/dialog'
import { router } from '@inertiajs/svelte'
let { open, user }: { open: boolean; user: User } = $props()
</script>
<Dialog
{open}
onOpenChange={(isOpen) => { if (!isOpen) router.replaceProp('show_dialog', false) }}
>
<DialogContent>
<DialogHeader>
<DialogTitle>{user.name}</DialogTitle>
</DialogHeader>
<!-- content -->
</DialogContent>
</Dialog>Svelte 4: instead of .
on:openChangeonOpenChangesvelte
<script lang="ts">
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '$lib/components/ui/dialog'
import { router } from '@inertiajs/svelte'
let { open, user }: { open: boolean; user: User } = $props()
</script>
<Dialog
{open}
onOpenChange={(isOpen) => { if (!isOpen) router.replaceProp('show_dialog', false) }}
>
<DialogContent>
<DialogHeader>
<DialogTitle>{user.name}</DialogTitle>
</DialogHeader>
<!-- 内容 -->
</DialogContent>
</Dialog>Svelte 4版本:使用替代。
on:openChangeonOpenChangeTable with Server-Side Sorting
带服务器端排序的表格
svelte
<script lang="ts">
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '$lib/components/ui/table'
import { router } from '@inertiajs/svelte'
let { users, sort }: { users: User[]; sort: string } = $props()
const handleSort = (column: string) => {
router.get('/users', { sort: column }, { preserveState: true })
}
</script>
<Table>
<TableHeader>
<TableRow>
<TableHead class="cursor-pointer" onclick={() => handleSort('name')}>
Name {sort === 'name' ? '↑' : ''}
</TableHead>
<TableHead>Email</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{#each users as user (user.id)}
<TableRow>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>Use or (not ) for row links to preserve SPA navigation.
<Link>use:inertia<a>svelte
<script lang="ts">
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '$lib/components/ui/table'
import { router } from '@inertiajs/svelte'
let { users, sort }: { users: User[]; sort: string } = $props()
const handleSort = (column: string) => {
router.get('/users', { sort: column }, { preserveState: true })
}
</script>
<Table>
<TableHeader>
<TableRow>
<TableHead class="cursor-pointer" onclick={() => handleSort('name')}>
姓名 {sort === 'name' ? '↑' : ''}
</TableHead>
<TableHead>邮箱</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{#each users as user (user.id)}
<TableRow>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>如需实现行链接,请使用或(而非)以保留SPA导航特性。
<Link>use:inertia<a>Toast with Flash Messages
结合Flash消息的提示框
Flash config () is in . Flash access
() is in . This section covers toast UI wiring only.
flash_keysinertia-rails-controllers$page.flashinertia-rails-pagesMANDATORY — READ ENTIRE FILE when implementing flash-based toasts with Sonner:
(~80 lines) — full flash
watcher and svelte-sonner integration. Do NOT load if only reading flash values without toast UI.
references/flash-toast.mdFlash配置()在中。Flash消息访问()在中。本节仅介绍提示框UI的关联方法。
flash_keysinertia-rails-controllers$page.flashinertia-rails-pages强制要求——实现基于Flash的Sonner提示框时,请完整阅读以下文件:
(约80行)——包含完整的Flash监听和svelte-sonner集成代码。如果仅需读取Flash值而无需提示框UI,请不要加载该文件。
references/flash-toast.mdDark Mode
深色模式
npx shadcn-svelte@latest init@custom-variant dark (&:is(.dark *));CRITICAL — prevent flash of wrong theme (FOUC): Add an inline script in
(before Svelte hydrates):
<head>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>Use a pattern (light/dark/system modes, localStorage persistence,
listener). Toggle via class on .
useAppearancematchMedia.dark<html>执行会生成浅色/深色模式的CSS变量,以及CSS中的(适用于Tailwind v4)。
npx shadcn-svelte@latest init@custom-variant dark (&:is(.dark *));关键注意事项——避免主题闪烁(FOUC): 在中添加内联脚本(在Svelte水合之前):
<head>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>使用模式(浅色/深色/系统模式、localStorage持久化、监听器)。通过元素的类切换主题。
useAppearancematchMedia<html>.dark<svelte:head>
Instead of <Head>
<svelte:head><Head>使用<svelte:head>
替代<Head>
<svelte:head><Head>Svelte uses native — there is no Inertia component for Svelte.
This applies in shadcn patterns too (e.g., setting page title in dialog views):
<svelte:head><Head>svelte
<svelte:head>
<title>{user.name} - Profile</title>
</svelte:head>Svelte使用原生的——Svelte中没有Inertia的组件。这同样适用于shadcn的模式(例如在对话框视图中设置页面标题):
<svelte:head><Head>svelte
<svelte:head>
<title>{user.name} - 个人资料</title>
</svelte:head>Svelte-Specific Gotchas
Svelte专属注意事项
bind:value<Form><Form>namebind:value<Form>svelte
<!-- BAD — bind:value is ignored by <Form> on submit -->
<Form method="post" action="/users">
<Input bind:value={name} />
</Form>
<!-- GOOD — name attribute is what <Form> reads -->
<Form method="post" action="/users">
<Input name="name" />
</Form>Use only with (where you explicitly manage ).
bind:valueuseForm$form.name$pagesvelte
<script lang="ts">
import { page } from '@inertiajs/svelte'
// BAD — snapshot, won't update after navigation:
// let user = $page.props.auth.user
// GOOD — use $derived for reactive access:
let user = $derived($page.props.auth.user)
</script>Svelte 4: use (reactive statement).
$: user = $page.props.auth.useruse:inertia<Link><Link>svelte
<script lang="ts">
import { inertia } from '@inertiajs/svelte'
</script>
<tr use:inertia={{ href: `/users/${user.id}` }} class="cursor-pointer">
<td>{user.name}</td>
</tr>bits-ui transition props and Inertia navigation — bits-ui components with
props may show stale content during Inertia page transitions if
the exit animation outlasts the navigation. Set short durations or use
on content that depends on page props.
transition*forceMountbind:value<Form><Form>namebind:value<Form>svelte
<!-- 错误用法——<Form>提交时会忽略bind:value -->
<Form method="post" action="/users">
<Input bind:value={name} />
</Form>
<!-- 正确用法——<Form>读取的是name属性值 -->
<Form method="post" action="/users">
<Input name="name" />
</Form>仅在使用时(需显式管理)才使用。
useForm$form.namebind:value$pagesvelte
<script lang="ts">
import { page } from '@inertiajs/svelte'
// 错误用法——仅为快照,导航后不会更新:
// let user = $page.props.auth.user
// 正确用法——使用$derived实现响应式访问:
let user = $derived($page.props.auth.user)
</script>Svelte 4版本:使用(响应式语句)。
$: user = $page.props.auth.useruse:inertia<Link><Link>svelte
<script lang="ts">
import { inertia } from '@inertiajs/svelte'
</script>
<tr use:inertia={{ href: `/users/${user.id}` }} class="cursor-pointer">
<td>{user.name}</td>
</tr>bits-ui过渡属性与Inertia导航——带有属性的bits-ui组件,在Inertia页面过渡期间,如果退出动画时长超过导航时长,可能会显示陈旧内容。请设置较短的动画时长,或对依赖页面props的内容使用。
transition*forceMountTroubleshooting
故障排除
| Symptom | Cause | Fix |
|---|---|---|
| Form components crash | Using shadcn-svelte form components that depend on superforms | 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 |
| | Use |
| Shared props stale after navigation | Destructured | Use |
| 症状 | 原因 | 解决方法 |
|---|---|---|
| 表单组件崩溃 | 使用了依赖superforms的shadcn-svelte表单组件 | 替换为普通的 |
| 缺少 | 为 |
| 对话框意外关闭 | | 使用 |
| 主题闪烁(FOUC) | | 在样式表之前添加深色模式脚本 |
| | 使用 |
| 共享props在导航后失效 | 解构 | 使用 |
Related Skills
相关技能
- Form component → +
inertia-rails-forms(references/svelte.mdsnippet, useForm)<Form> - Flash config → (flash_keys initializer)
inertia-rails-controllers - Flash access → +
inertia-rails-pages($page.flash)references/svelte.md - URL-driven dialogs → +
inertia-rails-pages(router.get pattern)references/svelte.md - directive →
use:inertia+inertia-rails-pagesreferences/svelte.md
- 表单组件 → +
inertia-rails-forms(references/svelte.md代码片段、useForm)<Form> - Flash配置 → (flash_keys初始化器)
inertia-rails-controllers - Flash访问 → +
inertia-rails-pages($page.flash)references/svelte.md - URL驱动的对话框 → +
inertia-rails-pages(router.get模式)references/svelte.md - 指令 →
use:inertia+inertia-rails-pagesreferences/svelte.md
References
参考资料
Load (~200 lines) when building
shadcn-svelte 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-svelte组件(如Accordion、Sheet、Tabs、DropdownMenu、结合Inertia模式的AlertDialog)时,请加载(约200行)。
references/components.md如果仅使用基础的Form、Select、Dialog或Table,请不要加载——上述示例已足够。
components.md