Using shadcn/ui Components
shadcn/ui provides beautifully designed React components that you own and copy into your project. Since shadcn/ui components use hooks and browser APIs, they must be "use client" components in ruact.
Prerequisites
Section titled “Prerequisites”- Tailwind CSS configured in your Rails app
- ruact installed and working (Getting Started)
Step 1: Set up shadcn/ui
Section titled “Step 1: Set up shadcn/ui”Initialize shadcn/ui in your project:
npx shadcn@latest initWhen prompted, choose the style, base color, and CSS variable settings. shadcn/ui will update your tailwind.config.js and create app/javascript/lib/utils.js.
Step 2: Add components
Section titled “Step 2: Add components”Use the shadcn CLI to add individual components:
npx shadcn@latest add buttonnpx shadcn@latest add dialognpx shadcn@latest add inputThis copies the component source into app/javascript/components/ui/:
app/javascript/components/ui/├── button.jsx├── dialog.jsx└── input.jsxStep 3: Mark components as “use client”
Section titled “Step 3: Mark components as “use client””shadcn/ui components use React hooks internally. You need to re-export them as "use client" wrappers so ruact can register them in the manifest.
Create app/javascript/components/Button.jsx:
"use client"
export { Button } from "./ui/button"Create app/javascript/components/Dialog.jsx:
"use client"
export { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog"Why a wrapper file? shadcn/ui components do not include
"use client"directives by default. The wrapper file is where you declare the boundary. The"use client"must be in the file that ruact scans — the one at the top of the import chain.
Step 4: Use components in ERB templates
Section titled “Step 4: Use components in ERB templates”<!-- Passes props from Ruby to the Button client component --><Button variant="outline" size="sm"> View Details</Button>
<!-- Open a dialog from server data --><Dialog> <DialogTrigger asChild> <Button>Open Post</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle><%= @post.title %></DialogTitle> </DialogHeader> <p><%= @post.body %></p> </DialogContent></Dialog>Step 5: Verify the manifest
Section titled “Step 5: Verify the manifest”After adding new components, rebuild the manifest:
# Development — Vite dev server rebuilds automatically on file savebin/dev
# Productionnpm run buildCheck that your components appear in the manifest:
cat public/react-client-manifest.json | grep -E "Button|Dialog"Passing complex props
Section titled “Passing complex props”shadcn/ui’s Select component requires structured option data. Pass it from Ruby:
<% options = @categories.map { |c| { value: c.id.to_s, label: c.name } }%><CategorySelect options={options} selectedValue={@post.category_id.to_s} />"use client"
import { useState } from "react"import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
export function CategorySelect({ options, selectedValue }) { const [value, setValue] = useState(selectedValue)
return ( <Select value={value} onValueChange={setValue}> <SelectTrigger> <SelectValue placeholder="Select a category" /> </SelectTrigger> <SelectContent> {options.map(opt => ( <SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem> ))} </SelectContent> </Select> )}Theming
Section titled “Theming”shadcn/ui uses CSS variables for theming. Configure them in your app/assets/stylesheets/application.css or app/javascript/application.css:
:root { --background: 0 0% 100%; --foreground: 240 10% 3.9%; --primary: 240 5.9% 10%; --primary-foreground: 0 0% 98%; /* ... other variables from shadcn init output */}
.dark { --background: 240 10% 3.9%; --foreground: 0 0% 98%; /* ... */}Tip: Keep ui/ components un-“use client”
Section titled “Tip: Keep ui/ components un-“use client””The files in ui/ (generated by shadcn) should remain as-is — do not add "use client" to them directly. This gives you the flexibility to:
- Import them into other client components without triggering extra manifest entries
- Keep generated shadcn files easy to update with
npx shadcn add - Control the public component API via your wrapper files in
components/