shadcn-rails
Rails components inspired by shadcn/ui, implemented with ViewComponent, Stimulus, Hotwire-friendly server-rendered markup, and Tailwind CSS.
What this is
- A Ruby gem,
shadcn-rails, that ships ViewComponent components for Rails apps. - An npm package,
shadcn-rails-stimulus, that ships the matching Stimulus controllers and CSS assets. - 55 documented component families, including buttons, forms, dialogs, menus, command, empty states, items, sidebar, and resizable panels.
- A Rails generator that can copy component source into your app so local files take precedence over the gem.
This is not a React component library and does not aim for full parity with upstream shadcn/ui. It is a Rails/ViewComponent port for server-rendered Rails applications.
If you use Inertia or React
Use the official shadcn/ui tooling instead:
npx shadcn init
npx shadcn add button dialogFor Rails + Inertia apps, start with inertia_rails and follow the official
Inertia Rails cookbook for integrating shadcn/ui. This gem does not add Inertia
or React support.
Name disambiguation
This repository is iheanyi/shadcn-rails and publishes the shadcn-rails Ruby
gem plus the shadcn-rails-stimulus npm package. It is unrelated to the
aviflombaum/shadcn-rails GitHub repository and the shadcn-ui Ruby gem.
Installation
Add the gem and run the installer:
bundle add shadcn-rails
rails generate shadcn:installFor importmap apps, the installer pins the local shadcn entrypoint plus its
external dependencies (@floating-ui/dom and stimulus-use) in
config/importmap.rb.
If your app bundles JavaScript or CSS through npm, add the Stimulus package:
npm install shadcn-rails-stimulus
# or
yarn add shadcn-rails-stimulusThen register all controllers:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails-stimulus"
const application = Application.start()
registerShadcnControllers(application)You can also register individual controllers:
import DialogController from "shadcn-rails-stimulus/controllers/dialog_controller"
application.register("shadcn--dialog", DialogController)Adding components to your app
The gem ships components that can be rendered directly. When you want to own and customize the source, copy component units into your application:
# List available components
rails generate shadcn:add --list
# Add specific components
rails generate shadcn:add button dialog tabs
# Hyphenated names are accepted too
rails generate shadcn:add dropdown-menu radio-group
# Add all available components
rails generate shadcn:add --all
# Add Ruby components without Stimulus controllers
rails generate shadcn:add dialog --exclude-controllersComponents are copied to app/components/shadcn/. Stimulus controllers are
copied to app/javascript/controllers/shadcn/. Compound components copy their
full unit, including subcomponent Ruby files, sidecar templates, controller
dependencies, and local JavaScript utilities needed by the copied controllers.
For example, rails generate shadcn:add dialog copies
dialog_component.rb, dialog_content_component.rb, dialog_header_component.rb,
dialog_title_component.rb, and the rest of the dialog unit.
Rails autoloading will prefer the local files in your application over the gem's built-in components.
Quick start
<%# Button %>
<%= render Shadcn::Button.new(variant: :default, class: "h-11") { "Click me" } %>
<%# Card %>
<%= render Shadcn::Card.new do |card| %>
<% card.with_header do |header| %>
<% header.with_title { "Welcome" } %>
<% end %>
<% card.with_content_slot do %>
<p>Your content here</p>
<% end %>
<% end %>
<%# Dialog %>
<%= render Shadcn::Dialog.new do |dialog| %>
<% dialog.with_trigger do %>
<%= render Shadcn::Button.new { "Open" } %>
<% end %>
<% dialog.with_body do |body| %>
<% body.with_header do |header| %>
<% header.with_title { "Edit Profile" } %>
<% end %>
<p>Dialog content here</p>
<% end %>
<% end %>Rails forms
Use Shadcn::FormBuilder with vanilla Rails form_with or form_for to render
shadcn-rails controls while preserving Rails field names, ids, nested
fields_for, hidden checkbox values, and submitted params:
<%= form_with model: @contact, builder: Shadcn::FormBuilder do |f| %>
<%= f.label :email %>
<%= f.email_field :email, placeholder: "you@example.com" %>
<%= f.text_area :notes %>
<%= f.check_box :subscribed %>
<%= f.select :status, Contact.statuses.keys %>
<%= f.submit "Save" %>
<% end %>form_for is older, but still supported:
<%= form_for @contact, builder: Shadcn::FormBuilder do |f| %>
<%= f.label :email %>
<%= f.email_field :email, placeholder: "you@example.com" %>
<%= f.check_box :subscribed %>
<%= f.select :status, Contact.statuses.keys %>
<%= f.submit "Save" %>
<% end %>To use it app-wide, configure Rails in your application controller:
class ApplicationController < ActionController::Base
default_form_builder Shadcn::FormBuilder
endThe gem does not set a default form builder automatically.
Components
| Category | Components |
|---|---|
| Actions | Button, Button Group, Toggle, Toggle Group |
| Forms | Checkbox, Field, Input, Input Group, Input OTP, Label, Native Select, Radio Group, Select, Slider, Switch, Textarea |
| Data Display | Aspect Ratio, Avatar, Badge, Card, Empty, Item, Kbd, Progress, Skeleton, Spinner, Table, Typography |
| Feedback | Alert, Toast, Tooltip |
| Overlays | Alert Dialog, Dialog, Drawer, Dropdown Menu, Hover Card, Popover, Sheet, Context Menu |
| Navigation | Accordion, Breadcrumb, Collapsible, Menubar, Navigation Menu, Pagination, Separator, Tabs, Resizable |
| Advanced | Calendar, Carousel, Combobox, Command, Date Picker, Sidebar |
Theming
shadcn-rails uses CSS custom properties for theming. Values use the HSL triplet format used by earlier shadcn/ui themes:
:root {
--radius: 0.75rem;
--primary: 221.2 83.2% 53.3%;
--destructive: 0 84% 60%;
}
.dark {
--primary: 217.2 91.2% 59.8%;
}Configure base colors in your initializer:
# config/initializers/shadcn.rb
Shadcn::Rails.configure do |config|
config.theme = :slate # :neutral, :slate, :zinc, :stone, :gray
config.radius = "0.5rem"
config.dark_mode = :class # :class, :media, :both
config.tailwind_prefix = ""
endconfig.base_color = "slate" remains supported as a deprecated alias for
config.theme = :slate.
The install generator adds the configured theme to your layout head:
<%= shadcn_theme %>For brand-level Tailwind CSS v4 overrides, keep the gem tokens in CSS and layer
your app's @theme values in your stylesheet:
@theme {
--color-primary: oklch(0.55 0.22 260);
}For Rails 8 with Tailwind CSS v4, import the shadcn styles linked by the installer:
@import "tailwindcss";
@import "./shadcn/base.css";
@import "./shadcn/components.css";
@import "./shadcn/tailwind-v4.css";Rails 8 with Tailwind CSS v4 is the supported install path. rails generate shadcn:install links app/assets/tailwind/shadcn to the gem's stylesheet
directory and adds those imports to app/assets/tailwind/application.css. The
installer still keeps a Tailwind v3 injection path for existing Rails apps that use
app/assets/stylesheets/application.tailwind.css.
With npm-based CSS bundling, import from the npm package:
@import "shadcn-rails-stimulus/styles/base";
@import "shadcn-rails-stimulus/styles/components";
@import "shadcn-rails-stimulus/styles/tailwind-v4";Requirements
- Ruby >= 3.1
- Rails >= 7.0
- Tailwind CSS v4 for the supported Rails 8 install path; v3 remains an installer fallback for existing apps
- Stimulus >= 3.0
- ViewComponent >= 3.0
Development
bundle install
cd test/dummy && bin/devVisit http://localhost:3000/docs for the component documentation.
Contributing
Bug reports and pull requests are welcome at https://github.com/iheanyi/shadcn-rails.
License
MIT License - see LICENSE for details.
Credits
- shadcn/ui - Original React component library
- ViewComponent - Ruby component framework
- Stimulus - JavaScript framework
- Tailwind CSS - Utility-first CSS