--- id: inertia-rails/skills/shadcn-svelte-inertia version: "3cbcbe23" license: MIT install: manual updated: 2026-02-13 --- # shadcn-svelte-inertia — Adapt shadcn-svelte (bits-ui) components for Inertia.js + Rails + Svelte apps by replacing SvelteKit patterns with Inertia equivalents. Wire inputs to Inertia Form via name attributes, manage dialogs and tables with Inertia router, and configure flash-based toasts with Rails initializers. Publisher: inertia-rails · Stars: 64 · Updated: 2026-02-13 Install (manual): `git clone https://github.com/inertia-rails/skills` ## SKILL.md # shadcn-svelte for Inertia Rails 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/navigation`, `load` functions, `+page.svelte`) → Replace with Inertia `router`, server props, page components - **Does it use `sveltekit-superforms` + `zod`?** → Replace with Inertia `
` + `name` attributes. Inertia handles CSRF, errors, redirects, processing state. ## Key Differences from SvelteKit Defaults | shadcn-svelte default (SvelteKit) | Inertia equivalent | |---|---| | `goto()` from `$app/navigation` | `router` from `@inertiajs/svelte` | | `load` functions | Server-rendered props via Rails controller | | `+page.svelte` / `+layout.svelte` | Default exports with module script layout | | `sveltekit-superforms` + `zod` | Inertia `` component | | `` (SvelteKit auto-manages) | `` (same — no Inertia `` in Svelte) | ## Setup `npx shadcn-svelte@latest init`. Add `@/` resolve aliases to `tsconfig.json` if not present. **Do NOT add `@/` resolve aliases to `vite.config.ts`** — `vite-plugin-ruby` already provides them. ## shadcn-svelte Inputs in Inertia `` Use plain shadcn-svelte `Input`/`Label`/`Button` with `name` attributes inside Inertia ``. See `inertia-rails-forms` skill (+ `references/svelte.md`) for full `` API. **The key pattern:** Use `{#snippet}` to access form state: ```svelte {#snippet children({ errors, processing })}
{#if errors.name}

{errors.name}

{/if}
{#if errors.email}

{errors.email}

{/if}
{/snippet} ``` Svelte 4: `
` instead of `{#snippet}`. **` Admin Member ``` ## Dialog with Inertia Navigation ```svelte { if (!isOpen) router.replaceProp('show_dialog', false) }} > {user.name} ``` Svelte 4: `on:openChange` instead of `onOpenChange`. ## Table with Server-Side Sorting ```svelte handleSort('name')}> Name {sort === 'name' ? '↑' : ''} Email {#each users as user (user.id)} {user.name} {user.email} {/each}
``` Use `` or `use:inertia` (not ``) for row links to preserve SPA navigation. ## Toast with Flash Messages Flash config (`flash_keys`) is in `inertia-rails-controllers`. Flash access (`$page.flash`) is in `inertia-rails-pages`. This section covers **toast UI wiring only**. **MANDATORY — READ ENTIRE FILE** when implementing flash-based toasts with Sonner: [`references/flash-toast.md`](references/flash-toast.md) (~80 lines) — full flash watcher and svelte-sonner integration. **Do NOT load** if only reading flash values without toast UI. ## Dark Mode `npx shadcn-svelte@latest init` generates CSS variables for light/dark and `@custom-variant dark (&:is(.dark *));` in your CSS (Tailwind v4). **CRITICAL — prevent flash of wrong theme (FOUC):** Add an inline script in `` (before Svelte hydrates): ```erb <%# app/views/layouts/application.html.erb — in , before any stylesheets %> ``` Use a `useAppearance` pattern (light/dark/system modes, localStorage persistence, `matchMedia` listener). Toggle via `.dark` class on ``. ## `` Instead of `` 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 {user.name} - Profile ``` ## Svelte-Specific Gotchas **`bind:value` does NOT work with Inertia ``** — `` reads values from input `name` attributes on submit, not from Svelte's reactive bindings. Using `bind:value` creates a second source of truth that `` ignores: ```svelte
``` Use `bind:value` only with `useForm` (where you explicitly manage `$form.name`). **`$page` store updates are reactive, but destructured values are not:** ```svelte ``` Svelte 4: use `$: user = $page.props.auth.user` (reactive statement). **`use:inertia` directive as alternative to ``** — for elements that can't be `` (e.g., table rows, custom components), use the action: ```svelte {user.name} ``` **bits-ui transition props and Inertia navigation** — bits-ui components with `transition*` props may show stale content during Inertia page transitions if the exit animation outlasts the navigation. Set short durations or use `forceMount` on content that depends on page props. ## Troubleshooting | Symptom | Cause | Fix | |---------|-------|-----| | Form components crash | Using shadcn-svelte form components that depend on superforms | Replace with plain `Input`/`Label` + `errors.field` display | | `Select` value not submitted | Missing `name` prop | Add `name="field"` to `