Loading...
Loading...
shadcn/ui component integration for Inertia Rails React (NOT Next.js): forms, dialogs, tables, toasts, dark mode, command palette, and more. Use when building UI with shadcn/ui components in an Inertia app or adapting shadcn examples from Next.js. NEVER react-hook-form/zod — wire shadcn inputs to Inertia Form via name attribute. Flash toasts require Rails flash_keys initializer config.
npx skill4agent add inertia-rails/skills shadcn-inertiareact-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>errorsnpx shadcn@latest init@/tsconfig.json@/vite.config.tsvite-plugin-ruby<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>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>flash_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>| 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) |
inertia-rails-forms<Form>inertia-rails-controllersinertia-rails-pagesinertia-rails-pagesreferences/components.mdcomponents.md