Docs
JavaScript

JavaScript

Rails defaults use Stimulus.js and Turbo from the Hotwire ecosystem. Rails UI follows these conventions and includes pre-built Stimulus controllers for common UI patterns.

railsui-stimulus

Rails UI includes railsui-stimulus, a library of JavaScript patterns we repeatedly use across themes and apps. It works independently of Rails UI and comes pre-installed with every theme.

The library includes 14 Stimulus controllers for common UI interactions:

Available Controllers

Clipboard

Copy content to the clipboard with visual feedback.

<div data-controller="railsui-clipboard">
  <input type="text" value="Text to copy" data-railsui-clipboard-target="source" readonly>
  <button data-action="click->railsui-clipboard#copy">
    Copy
  </button>
</div>

Count Up

Animate numbers counting up to a target value. Great for statistics and metrics.

<span data-controller="railsui-count-up"
      data-railsui-count-up-target-value="1000"
      data-railsui-count-up-duration-value="2000">
  0
</span>

Combobox

Searchable dropdown combining an input field with selectable options.

<div data-controller="railsui-combobox">
  <input type="text"
         data-railsui-combobox-target="input"
         data-action="input->railsui-combobox#filter focus->railsui-combobox#open"
         placeholder="Search...">
  <ul data-railsui-combobox-target="list" class="hidden">
    <li data-action="click->railsui-combobox#select">Option 1</li>
    <li data-action="click->railsui-combobox#select">Option 2</li>
    <li data-action="click->railsui-combobox#select">Option 3</li>
  </ul>
</div>

Date Range Picker

Select date ranges with an interactive calendar. Requires flatpickr.

<div data-controller="railsui-date-range-picker">
  <input type="text"
         data-railsui-date-range-picker-target="input"
         placeholder="Select date range">
</div>

Password Toggle

Toggle password field visibility between masked and plain text.

<div data-controller="railsui-password-toggle">
  <input type="password"
         data-railsui-password-toggle-target="input">
  <button data-action="click->railsui-password-toggle#toggle">
    Show/Hide
  </button>
</div>

Range

Slider input with visual fill indicator.

<div data-controller="railsui-range">
  <input type="range"
         data-railsui-range-target="input"
         data-action="input->railsui-range#update"
         min="0" max="100" value="50">
  <span data-railsui-range-target="value">50</span>
</div>

Read More

Expandable/collapsible text sections with truncated content.

<div data-controller="railsui-read-more">
  <div data-railsui-read-more-target="content" class="line-clamp-3">
    Long content that will be truncated initially...
  </div>
  <button data-action="click->railsui-read-more#toggle"
          data-railsui-read-more-target="button">
    Read more
  </button>
</div>

Select All

Bulk selection for checkboxes or list items.

<div data-controller="railsui-select-all">
  <label>
    <input type="checkbox"
           data-railsui-select-all-target="selectAll"
           data-action="change->railsui-select-all#toggleAll">
    Select All
  </label>

  <label>
    <input type="checkbox"
           data-railsui-select-all-target="checkbox"
           data-action="change->railsui-select-all#updateSelectAll">
    Item 1
  </label>
  <label>
    <input type="checkbox"
           data-railsui-select-all-target="checkbox"
           data-action="change->railsui-select-all#updateSelectAll">
    Item 2
  </label>
</div>

Tabs

Tabbed navigation for organizing content into panels.

<div data-controller="railsui-tabs" data-railsui-tabs-active-class="active">
  <div role="tablist">
    <button data-railsui-tabs-target="tab"
            data-action="click->railsui-tabs#select">Tab 1</button>
    <button data-railsui-tabs-target="tab"
            data-action="click->railsui-tabs#select">Tab 2</button>
  </div>

  <div data-railsui-tabs-target="panel">Content for Tab 1</div>
  <div data-railsui-tabs-target="panel" class="hidden">Content for Tab 2</div>
</div>

Toast

Temporary notification messages that auto-dismiss.

<div data-controller="railsui-toast"
     data-railsui-toast-delay-value="3000">
  <div data-railsui-toast-target="toast">
    <p>This message will auto-dismiss in 3 seconds.</p>
    <button data-action="click->railsui-toast#dismiss">Dismiss</button>
  </div>
</div>

Toggle

Basic toggle switches for boolean state management.

<div data-controller="railsui-toggle">
  <button data-action="click->railsui-toggle#toggle">
    Toggle Content
  </button>
  <div data-railsui-toggle-target="toggleable" class="hidden">
    Hidden content that can be toggled.
  </div>
</div>

Tooltip

Contextual help text on hover. Uses Tippy.js for positioning.

<span data-controller="railsui-tooltip"
      data-railsui-tooltip-content-value="Helpful tooltip text"
      data-railsui-tooltip-placement-value="top">
  Hover me
</span>

Installation

Every Rails UI theme includes railsui-stimulus pre-configured. If you need to install it separately:

Importmap (nobuild)

<span data-controller="railsui-tooltip"
      data-railsui-tooltip-content-value="Helpful tooltip text"
      data-railsui-tooltip-placement-value="top">
  Hover me
</span>
./bin/importmap pin railsui-stimulus

Then register the controllers in your JavaScript:

<span data-controller="railsui-tooltip"
      data-railsui-tooltip-content-value="Helpful tooltip text"
      data-railsui-tooltip-placement-value="top">
  Hover me
</span>
./bin/importmap pin railsui-stimulus
// app/javascript/controllers/index.js
import { application } from "./application"

import { RailsuiClipboard, RailsuiDropdown, RailsuiModal, RailsuiTabs }
  from "railsui-stimulus"

application.register("railsui-clipboard", RailsuiClipboard)
application.register("railsui-dropdown", RailsuiDropdown)
application.register("railsui-modal", RailsuiModal)
application.register("railsui-tabs", RailsuiTabs)
// ... register others as needed

NPM/Yarn (build mode)

<span data-controller="railsui-tooltip"
      data-railsui-tooltip-content-value="Helpful tooltip text"
      data-railsui-tooltip-placement-value="top">
  Hover me
</span>
npm install railsui-stimulus
# or
yarn add railsui-stimulus
// app/javascript/controllers/index.js
import { application } from "./application"

import { RailsuiClipboard, RailsuiDropdown, RailsuiModal, RailsuiTabs }
  from "railsui-stimulus"

application.register("railsui-clipboard", RailsuiClipboard)
application.register("railsui-dropdown", RailsuiDropdown)
application.register("railsui-modal", RailsuiModal)
application.register("railsui-tabs", RailsuiTabs)
// ... register others as needed

Extending Controllers

You can extend any controller to add custom behavior:

<span data-controller="railsui-tooltip"
      data-railsui-tooltip-content-value="Helpful tooltip text"
      data-railsui-tooltip-placement-value="top">
  Hover me
</span>
npm install railsui-stimulus
# or
yarn add railsui-stimulus
// app/javascript/controllers/custom_dropdown_controller.js
import { RailsuiDropdown } from "railsui-stimulus"

export default class extends RailsuiDropdown {
  connect() {
    super.connect()
    // Add custom initialization
  }

  toggle() {
    super.toggle()
    // Add custom behavior after toggle
    console.log("Dropdown toggled!")
  }
}

Turbo Compatibility

All controllers are designed to work seamlessly with Turbo. They properly clean up on disconnect and reinitialize when Turbo navigates to new pages or updates frames.

When using Turbo Frames or Turbo Streams, controllers automatically connect to new elements as they're added to the DOM.

Get all updates directly to your inbox.
Sign up for the newsletter.

    We won't send you spam. Unsubscribe at any time.