ViewPrimitives

Gem Version Build Status

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
rails g view_primitives:install --force   # refresh ApplicationComponent and CSS on upgrade

This will:

  • Create app/components/application_component.rb with ViewPrimitives::ClassHelper, cn(), and extract_html_attrs (skipped if you already have one — use --force to overwrite)
  • Create app/components/ui/styles.rb with shared Tailwind primitive class names (UI::Styles::*)
  • Create app/assets/stylesheets/view_primitives.css (entry file) plus view_primitives/tokens.css, utilities.css, and themes/default.css

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.

CSS layout

app/assets/stylesheets/
  view_primitives.css          # entry — @import tokens, utilities, themes
  view_primitives/
    tokens.css                 # @theme inline design tokens
    utilities.css              # vp-* shared primitives
    themes/
      default.css              # :root and .dark color variables
      rose.css                 # optional — via view_primitives:theme

Generators

Generator Purpose
view_primitives:install Base setup — ApplicationComponent, styles.rb, CSS bundle
view_primitives:add Copy one or more components into the app
view_primitives:update Refresh installed components, CSS, and styles.rb from the gem
view_primitives:theme Install an optional color theme
view_primitives:list List available and installed components

See docs/components/README.md for setup details, Stimulus notes, and class: overrides.

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.

Component docs live in docs/components/. Start with component setup if you have not run install yet.

Updating installed components

After upgrading the gem, refresh every component you have already copied:

rails g view_primitives:update
rails g view_primitives:update --only button input   # selected components
rails g view_primitives:update --skip-css            # components only

The update generator overwrites installed component files, the CSS bundle, and styles.rb from the latest gem templates. Back up local edits first.

Optional color themes

rails g view_primitives:theme rose

This copies the theme CSS and enables its @import in view_primitives.css. Apply it with data-theme="rose" on your layout root (.dark still works for dark mode).

Optional tailwind_merge

cn() deduplicates class strings. For Tailwind-aware conflict resolution, add the tailwind_merge gem to your Gemfile — ViewPrimitives picks it up automatically when present.

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
Tags Input Multi-select input that renders selected values as removable chips 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
  • Shared UI::Styles primitives and border conventions (border-border, MENU_SEPARATOR)
  • 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.