Skip to main content

Toggle Group Component for Rails

Toggle Group component for Rails. A group of two-state buttons that can be toggled on or off, with single and multiple selection modes.

Usage

<%= render "components/toggle_group", type: :single, value: "center" do %>
  <%= render "components/toggle_group/item", value: "left", aria_label: "Align left" do %>
    <%= icon_for :align_left, class: "size-4" %>
  <% end %>
  <%= render "components/toggle_group/item", value: "center", aria_label: "Align center", pressed: true do %>
    <%= icon_for :align_center, class: "size-4" %>
  <% end %>
  <%= render "components/toggle_group/item", value: "right", aria_label: "Align right" do %>
    <%= icon_for :align_right, class: "size-4" %>
  <% end %>
<% end %>

Examples

Multiple Selection

<%= render "components/toggle_group", type: :multiple, value: ["bold", "italic"] do %>
  <%= render "components/toggle_group/item", value: "bold", aria_label: "Bold", pressed: true do %>
    <%= icon_for :bold, class: "size-4" %>
  <% end %>
  <%= render "components/toggle_group/item", value: "italic", aria_label: "Italic", pressed: true do %>
    <%= icon_for :italic, class: "size-4" %>
  <% end %>
  <%= render "components/toggle_group/item", value: "underline", aria_label: "Underline" do %>
    <%= icon_for :underline, class: "size-4" %>
  <% end %>
<% end %>

Outline Variant

<%= render "components/toggle_group", type: :single, variant: :outline do %>
  <%= render "components/toggle_group/item", value: "list", aria_label: "List view" do %>
    <%= icon_for :list, class: "size-4" %>
  <% end %>
  <%= render "components/toggle_group/item", value: "grid", aria_label: "Grid view" do %>
    <%= icon_for :grid, class: "size-4" %>
  <% end %>
<% end %>

With Text Labels

The group's value is the source of truth: on connect the controller re-syncs every item from it, so an item marked pressed: true that is not in value turns off. Keep the two in agreement (pressed only sets the markup rendered before JavaScript runs).

<%= render "components/toggle_group", type: :single, size: :lg, value: "day" do %>
  <%= render "components/toggle_group/item", value: "day", pressed: true do %>
    Day
  <% end %>
  <%= render "components/toggle_group/item", value: "week" do %>
    Week
  <% end %>
  <%= render "components/toggle_group/item", value: "month" do %>
    Month
  <% end %>
<% end %>

Disabled

Pass disabled: true to the group to disable every item. The group gets aria-disabled="true", and the controller sets disabled on each item, so items rendered directly from partials leave the tab order and ignore clicks and keys. The helpers also render each item with disabled, so it works before JavaScript loads.

<%= render "components/toggle_group", type: :single, value: "left", disabled: true do %>
  <%= render "components/toggle_group/item", value: "left", aria_label: "Align left", pressed: true do %>
    <%= icon_for :align_left, class: "size-4" %>
  <% end %>
  <%= render "components/toggle_group/item", value: "right", aria_label: "Align right" do %>
    <%= icon_for :align_right, class: "size-4" %>
  <% end %>
<% end %>

Listening for Changes

<%= render "components/toggle_group", type: :single, value: "day",
      data: { action: "toggle-group:change->calendar#changeView" } do %>
  <%# items %>
<% end %>

API Reference

Toggle Group

Parameter Type Default Description
type Symbol :single :single or :multiple selection
variant Symbol :default :default or :outline
size Symbol :default :sm, :default, :lg
value String/Array nil Initial selected value(s); drives each item's pressed state once the controller connects
disabled Boolean false Disables the whole group: sets aria-disabled="true" on the group, and the controller sets disabled on every item (the helpers also render each item disabled)
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Toggle Group Item

Parameter Type Default Description
value String required Value when selected, rendered as data-value
pressed Boolean false Initial pressed state (data-state="on", aria-pressed); overridden by the group's value on connect
disabled Boolean false Disable this item
aria_label String nil Accessible label for icon-only items
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Builder Helper

The toggle_group helper collects items and renders them inside the group, marking items pressed from the group's value:

<%= toggle_group type: :multiple, variant: :outline, value: ["bold"] do |group| %>
  <% group.item value: "bold", icon: :bold, aria_label: "Toggle bold" %>
  <% group.item value: "italic", icon: :italic, aria_label: "Toggle italic" %>
  <% group.item value: "underline", aria_label: "Underline" do %>
    <%= icon_for :underline, class: "size-4" %>
  <% end %>
<% end %>

Or fully data-driven with toggle_group_simple, passing an array of item hashes:

<%= toggle_group_simple type: :single, value: "center", items: [
  { value: "left", icon: :align_left, aria_label: "Align left" },
  { value: "center", icon: :align_center, aria_label: "Align center" },
  { value: "right", icon: :align_right, aria_label: "Align right" }
] %>

Helper Methods

Method Description
toggle_group(type: :single, variant: :default, size: :default, value: nil, disabled: false, css_classes: "", **html_options, &block) With a one-argument block, yields a builder; otherwise renders the block as the group's content
group.item(value:, label: nil, icon: nil, disabled: false, aria_label: nil, **options, &block) Builder item. Renders the block, or the icon (via icon_for) followed by the label. Pressed when value is in the group's value; disabled when it or the group is. Extra options are HTML attributes
toggle_group_simple(items:, type: :single, variant: :default, size: :default, value: nil, disabled: false, css_classes: "", **html_options) Renders items from hashes with :value, :label, :icon, :disabled, :aria_label

JavaScript

The group carries data-controller="toggle-group"; each item is an item target wired to click->toggle-group#toggle and keydown->toggle-group#handleKeydown.

Value Type Default Description
type String "single" single or multiple, from the type local
selected Array [] Selected values, from the value local
Action Description
toggle Toggles the clicked item. In single mode clicking the pressed item clears the selection
handleKeydown Arrow keys move focus between enabled items (wrapping), Home and End jump to the first and last. Does nothing in a disabled group

On every change the controller dispatches one toggle-group:change event from the group element (bubbling) with detail: { type, value }, where value is a string or null in single mode and an array in multiple mode.

When the group has aria-disabled="true", the controller sets disabled on every item target as it connects, including items added later.

The controller also exposes select(value), deselect(value), clear(), and getValue() for use from other controllers via outlets.