ViewPrimitives
A shadcn/ui-inspired component library for Rails built on ViewComponent.
Acknowledgements — The visual design, CSS class choices, and component structure of ViewPrimitives are heavily inspired by shadcn/ui and its Svelte port shadcn-svelte. We are grateful to @shadcn and all contributors for their outstanding open-source work. ViewPrimitives is an independent Rails adaptation and is not affiliated with or endorsed by the shadcn/ui project.
Components are copied into your app via a generator — not imported from a package. Tailwind classes live in your own files, so any Tailwind setup works out of the box: tailwindcss-rails, cssbundling-rails, Vite, esbuild — no configuration required.
Requirements
- Ruby >= 3.2 (developed with 4.0.5 — use mise or see
.ruby-version) - Rails >= 7.1 (required by ViewComponent 4)
- ViewComponent >= 4.0
- Tailwind CSS (any setup)
Installation
Add to your Gemfile:
gem "view_primitives"
Then run the install generator:
rails g view_primitives:install
This will:
- Create
app/components/application_component.rbwithViewPrimitives::ClassHelperincluded (skipped if you already have one — addinclude ViewPrimitives::ClassHelpermanually) - Create
app/assets/stylesheets/view_primitives.csswith the design token definitions (@theme inline+ oklch light/dark theme)
Then import it in your Tailwind CSS entry point:
/* tailwindcss-rails → app/assets/tailwind/application.css */
/* tailwind (legacy) → app/assets/stylesheets/application.tailwind.css */
/* cssbundling/Vite → app/javascript/application.css */
@import "./view_primitives";
The install generator auto-detects these entry points and injects the import when possible.
UI namespace
Components live under UI:: (files in app/components/ui/). The gem registers the UI acronym with ActiveSupport so ui :button resolves to UI::ButtonComponent.
That's it — no tailwind.config.js required. Tailwind 4 reads the @theme inline block directly from the CSS.
Adding components
rails g view_primitives:list # available + installed status
rails g view_primitives:add button
rails g view_primitives:add button alert accordion # multiple at once
Each component is copied into app/components/ui/ as plain Ruby and ERB files you own and can modify freely. Re-running add overwrites existing files (a warning is printed). Unknown component names fail with a non-zero exit code.
View helpers
ViewPrimitives adds the ui helper to views and mailers:
<%# Positional label — no block needed %>
<%= ui :button, "Save changes", variant: :outline %>
<%= ui :alert, title: "Heads up!", description: "Check your settings." %>
<%= ui :accordion, items: [{ title: "FAQ", content: "Answer here." }] %>
<%# Block — for icons, slots, or complex content %>
<%= ui :button do %><svg .../> Save<% end %>
<%= ui :alert do |a| %><% a.with_alert_title { "Note" } %><% end %>
ui is shorthand for render UI::SomeComponent.new(...). For components outside app/components/ui/, use render as usual.
Components
Available now
| Component | Description | Docs |
|---|---|---|
| Button | Clickable element with 6 variants and 4 sizes | docs |
| Alert | Informational banner with title and description slots | docs |
| Accordion | Collapsible sections via native <details>, optional exclusive mode |
docs |
| Badge | Small status label with variants | docs |
| Avatar | User avatar with image and initials fallback | docs |
| Card | Container with header, content, and footer slots | docs |
| Separator | Horizontal or vertical divider | docs |
| Label | Accessible form label | docs |
| Skeleton | Loading placeholder with pulse animation | docs |
| Progress | Progress bar with value prop | docs |
| Aspect Ratio | Constrains child content to a given aspect ratio | docs |
| Spinner | Animated loading indicator | docs |
| KBD | Keyboard shortcut key display | docs |
| Rating | Read-only star rating display | docs |
| Rating Input | Interactive star rating — form or AJAX submission | docs |
| Indicator | Status dot or count badge overlaid on an element | docs |
| List Group | Bordered list with optional links and active state | docs |
| Banner | Styled announcement strip with variants | docs |
| Button Group | Visually joined row of buttons | docs |
| Input | Styled text input with ring/border | docs |
| Textarea | Styled multi-line input | docs |
| Checkbox | Accessible checkbox with optional label | docs |
| Radio Group | Group of radio inputs | docs |
| Select | Native styled select element | docs |
| Switch | CSS-only on/off toggle | docs |
| Toggle | Single pressable toggle button | docs |
| Toggle Group | Group of related toggles (single or multiple) | docs |
| Form Field | Label + input + hint + error layout wrapper | docs |
| File Input | Styled file upload input | docs |
| Search Input | Text input with built-in search icon and clear button | docs |
| Number Input | Text input with increment/decrement controls | docs |
| Range | Styled range slider | docs |
| Floating Label | Input with floating placeholder label | docs |
| Breadcrumb | Navigational breadcrumb trail with separator | docs |
| Pagination | Page number links with prev/next and ellipsis | docs |
| Stepper | Multi-step progress indicator (horizontal + vertical) | docs |
| Tabs | Tab bar with content panels (array API + slot API) | docs |
| Navbar | Responsive top navigation bar with hamburger menu | docs |
| Navigation Menu | Top-level navigation with optional dropdown flyouts | docs |
| Bottom Nav | Mobile-style tab bar fixed to the bottom | docs |
| Footer | Page footer with columns, links, and copyright | docs |
| Mega Menu | Full-width dropdown panel with grouped links and images | docs |
| Dialog | Modal dialog with trigger, title, description, footer slots | docs |
| Alert Dialog | Blocking confirmation dialog | docs |
| Sheet | Slide-in panel from any edge (left/right/top/bottom) | docs |
| Drawer | Bottom sheet with drag handle — mobile drawer pattern | docs |
| Popover | Floating panel anchored to a trigger | docs |
| Tooltip | Hover label — CSS-only, no JS | docs |
| Hover Card | Rich hover preview card — CSS-only, no JS | docs |
| Dropdown Menu | Trigger-anchored menu with items and separators | docs |
| Context Menu | Right-click context menu positioned at cursor | docs |
| Menubar | Horizontal application-style menu bar | docs |
| Command | Modal command palette with live search filtering | docs |
| Combobox | Autocomplete select with live search | docs |
| Calendar | Date picker calendar grid | docs |
| Date Picker | Input that opens a Calendar popover | docs |
| Timepicker | Input for selecting a time value | docs |
| Carousel | Scrollable item carousel with prev/next controls | docs |
| Data Table | Sortable, filterable table with pagination | docs |
| Sidebar | Collapsible application sidebar with nav groups | docs |
| Input OTP | One-time-password digit input group | docs |
| Collapsible | Single collapsible section (simpler than Accordion) | docs |
| Resizable | Drag-to-resize panel layout | docs |
| Scroll Area | Custom scrollbar container | docs |
| Gallery | Responsive image grid with optional lightbox | docs |
| Chat Bubble | Styled message bubble for chat or comment threads | docs |
| Speed Dial | Floating action button that expands into sub-actions | docs |
| Device Mockup | Phone or browser frame for marketing screenshots | docs |
| QR Code | QR code display from a given value | docs |
| Timeline | Vertical timeline with event items | docs |
| Toaster | Stacked toast notifications (Sonner-style) | docs |
| Chart | Chart.js adapter — bar, line, pie, doughnut, radar, polar area | docs |
| Picture | <picture> + <source> for art direction and modern formats (AVIF/WebP) |
docs |
| Video | <video> + <source> with poster, controls, and caption tracks |
docs |
| Figure | <figure> + <figcaption> wrapper for media content |
docs |
| Image | Responsive <img> with srcset / sizes |
docs |
| Audio | <audio> + <source> with optional transcript link |
docs |
| Iframe | Sandboxed embed wrapper with required title and lazy loading |
docs |
| WYSIWYG | Rich-text editor — Trix (default) or Quill adapter | docs |
| Map / Area | Image map with clickable <area> regions |
docs |
| Embed | Third-party embeds — YouTube, Vimeo, Spotify, Google Maps, Yandex Maps, Loom, SoundCloud, X, Telegram, Facebook | docs |
See ROADMAP.md for the full component list organised by phase.
Customisation
See docs/customization.md for the full guide covering:
- Design tokens (OKLCH colors, radius) — change the whole palette in one file
- Editing component constants — add variants, change classes
- Per-instance
class:overrides — append utilities without touching the file - Full brand theming example
Development
bin/setup # install dependencies
bundle exec rake # run tests + linter
bin/console # interactive prompt
To run tests against a specific Rails version:
bundle exec appraisal rails-8.1 rake test
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/alec-c4/view_primitives.
License
MIT License. See LICENSE.txt.