Select

Displays a list of options for the user to pick from—triggered by a button.

Requires JavaScript
select/default
<%= render(Shadcn::SelectComponent.new(name: "fruit", placeholder: "Select a fruit")) do |select| %>
  <% select.with_item(value: "apple") { "Apple" } %>
  <% select.with_item(value: "banana") { "Banana" } %>
  <% select.with_item(value: "orange") { "Orange" } %>
  <% select.with_item(value: "grape") { "Grape" } %>
  <% select.with_item(value: "strawberry") { "Strawberry" } %>
<% end %>

Installation

Add the component to your project:

rails generate shadcn:add select

Usage

<%= render(Shadcn::SelectComponent.new(name: "fruit", placeholder: "Select a fruit")) do |select| %>
  <% select.with_item(value: "apple") { "Apple" } %>
  <% select.with_item(value: "banana") { "Banana" } %>
  <% select.with_item(value: "orange") { "Orange" } %>
  <% select.with_item(value: "grape") { "Grape" } %>
  <% select.with_item(value: "strawberry") { "Strawberry" } %>
<% end %>

Examples

With Groups

Organize options into labeled groups.

<%= render Shadcn::SelectComponent.new(name: "timezone", placeholder: "Select a timezone", class_name: "w-[200px]") do |select| %>
  <% select.with_group(label: "North America") do |group| %>
    <% group.with_item(value: "est") { "Eastern Time" } %>
    <% group.with_item(value: "cst") { "Central Time" } %>
    <% group.with_item(value: "mst") { "Mountain Time" } %>
    <% group.with_item(value: "pst") { "Pacific Time" } %>
  <% end %>
  <% select.with_group(label: "Europe") do |group| %>
    <% group.with_item(value: "gmt") { "Greenwich Mean Time" } %>
    <% group.with_item(value: "cet") { "Central European Time" } %>
  <% end %>
<% end %>

Preselected Value

Set an initial value with the value parameter.

<%= render Shadcn::Select.new(
  name: "fruit",
  value: "banana",
  placeholder: "Select a fruit",
  class_name: "w-[180px]"
) do |select| %>
  <% select.with_item(value: "apple") { "Apple" } %>
  <% select.with_item(value: "banana") { "Banana" } %>
  <% select.with_item(value: "orange") { "Orange" } %>
<% end %>

Disabled State

Disable the entire select component.

<%= render Shadcn::SelectComponent.new(name: "fruit", placeholder: "Select a fruit", disabled: true, class_name: "w-[180px]") do |select| %>
  <% select.with_item(value: "apple") { "Apple" } %>
  <% select.with_item(value: "banana") { "Banana" } %>
  <% select.with_item(value: "orange") { "Orange" } %>
<% end %>

Disabled Items

Disable specific items within the select.

<%= render Shadcn::SelectComponent.new(name: "fruit", placeholder: "Select a fruit", class_name: "w-[180px]") do |select| %>
  <% select.with_item(value: "apple") { "Apple" } %>
  <% select.with_item(value: "banana", disabled: true) { "Banana (out of stock)" } %>
  <% select.with_item(value: "orange") { "Orange" } %>
  <% select.with_item(value: "grape", disabled: true) { "Grape (out of stock)" } %>
  <% select.with_item(value: "pineapple") { "Pineapple" } %>
<% end %>

Form Integration

Use Select within forms with proper labels and validation.

<%= form_with url: "#", method: :post, class: "flex flex-col gap-4" do |f| %>
  <div class="flex flex-col gap-2">
    <%= render Shadcn::Label.new(for: "country") { "Country" } %>
    <%= render Shadcn::Select.new(
      name: "country",
      id: "country",
      placeholder: "Select your country",
      required: true,
      class_name: "w-[200px]"
    ) do |select| %>
      <% select.with_item(value: "us") { "United States" } %>
      <% select.with_item(value: "uk") { "United Kingdom" } %>
      <% select.with_item(value: "ca") { "Canada" } %>
      <% select.with_item(value: "au") { "Australia" } %>
    <% end %>
  </div>
  <%= render Shadcn::Button.new(type: :submit) { "Submit" } %>
<% end %>

With Default Value

select/with_default_value
<%= render(Shadcn::SelectComponent.new(name: "timezone", value: "est", placeholder: "Select a timezone")) do |select| %>
  <% select.with_item(value: "est") { "Eastern Standard Time (EST)" } %>
  <% select.with_item(value: "cst") { "Central Standard Time (CST)" } %>
  <% select.with_item(value: "mst") { "Mountain Standard Time (MST)" } %>
  <% select.with_item(value: "pst") { "Pacific Standard Time (PST)" } %>
<% end %>

Disabled Options

select/disabled_options
<%= render(Shadcn::SelectComponent.new(name: "plan", placeholder: "Select a plan")) do |select| %>
  <% select.with_item(value: "free") { "Free" } %>
  <% select.with_item(value: "starter") { "Starter" } %>
  <% select.with_item(value: "pro") { "Pro" } %>
  <% select.with_item(value: "enterprise", disabled: true) { "Enterprise (Coming Soon)" } %>
<% end %>

Disabled Select

select/disabled_select
<%= render(Shadcn::SelectComponent.new(name: "status", disabled: true, placeholder: "Select status")) do |select| %>
  <% select.with_item(value: "active") { "Active" } %>
  <% select.with_item(value: "inactive") { "Inactive" } %>
  <% select.with_item(value: "pending") { "Pending" } %>
<% end %>

Required Field

select/required_field
<%= render(Shadcn::SelectComponent.new(name: "country", required: true, placeholder: "Select a country")) do |select| %>
  <% select.with_item(value: "us") { "United States" } %>
  <% select.with_item(value: "ca") { "Canada" } %>
  <% select.with_item(value: "uk") { "United Kingdom" } %>
  <% select.with_item(value: "au") { "Australia" } %>
<% end %>

Framework Selection

select/framework_selection
<%= render(Shadcn::SelectComponent.new(name: "framework", placeholder: "Select a framework", class_name: "w-[280px]")) do |select| %>
  <% select.with_item(value: "next") { "Next.js" } %>
  <% select.with_item(value: "sveltekit") { "SvelteKit" } %>
  <% select.with_item(value: "nuxt") { "Nuxt.js" } %>
  <% select.with_item(value: "remix") { "Remix" } %>
  <% select.with_item(value: "astro") { "Astro" } %>
  <% select.with_item(value: "rails") { "Ruby on Rails" } %>
<% end %>

Email Provider

select/email_provider
<%= render(Shadcn::SelectComponent.new(name: "email", value: "gmail", placeholder: "Select email")) do |select| %>
  <% select.with_group(label: "Personal") do |group| %>
    <% group.with_item(value: "gmail") { "Gmail" } %>
    <% group.with_item(value: "yahoo") { "Yahoo Mail" } %>
    <% group.with_item(value: "outlook") { "Outlook" } %>
    <% group.with_item(value: "icloud") { "iCloud Mail" } %>
  <% end %>
  <% select.with_group(label: "Business") do |group| %>
    <% group.with_item(value: "gsuite") { "Google Workspace" } %>
    <% group.with_item(value: "office365") { "Microsoft 365" } %>
    <% group.with_item(value: "zoho") { "Zoho Mail" } %>
  <% end %>
<% end %>

Long List

select/long_list
<%= render Shadcn::Select.new(name: "number", placeholder: "Select a number") do |select| %>
  <% select.with_item(value: "1") { "Option 1" } %>
  <% select.with_item(value: "2") { "Option 2" } %>
  <% select.with_item(value: "3") { "Option 3" } %>
  <% select.with_item(value: "4") { "Option 4" } %>
  <% select.with_item(value: "5") { "Option 5" } %>
  <% select.with_item(value: "6") { "Option 6" } %>
  <% select.with_item(value: "7") { "Option 7" } %>
  <% select.with_item(value: "8") { "Option 8" } %>
  <% select.with_item(value: "9") { "Option 9" } %>
  <% select.with_item(value: "10") { "Option 10" } %>
  <% select.with_item(value: "11") { "Option 11" } %>
  <% select.with_item(value: "12") { "Option 12" } %>
  <% select.with_item(value: "13") { "Option 13" } %>
  <% select.with_item(value: "14") { "Option 14" } %>
  <% select.with_item(value: "15") { "Option 15" } %>
  <% select.with_item(value: "16") { "Option 16" } %>
  <% select.with_item(value: "17") { "Option 17" } %>
  <% select.with_item(value: "18") { "Option 18" } %>
  <% select.with_item(value: "19") { "Option 19" } %>
  <% select.with_item(value: "20") { "Option 20" } %>
  <% select.with_item(value: "21") { "Option 21" } %>
  <% select.with_item(value: "22") { "Option 22" } %>
  <% select.with_item(value: "23") { "Option 23" } %>
  <% select.with_item(value: "24") { "Option 24" } %>
  <% select.with_item(value: "25") { "Option 25" } %>
  <% select.with_item(value: "26") { "Option 26" } %>
  <% select.with_item(value: "27") { "Option 27" } %>
  <% select.with_item(value: "28") { "Option 28" } %>
  <% select.with_item(value: "29") { "Option 29" } %>
  <% select.with_item(value: "30") { "Option 30" } %>
  <% select.with_item(value: "31") { "Option 31" } %>
  <% select.with_item(value: "32") { "Option 32" } %>
  <% select.with_item(value: "33") { "Option 33" } %>
  <% select.with_item(value: "34") { "Option 34" } %>
  <% select.with_item(value: "35") { "Option 35" } %>
  <% select.with_item(value: "36") { "Option 36" } %>
  <% select.with_item(value: "37") { "Option 37" } %>
  <% select.with_item(value: "38") { "Option 38" } %>
  <% select.with_item(value: "39") { "Option 39" } %>
  <% select.with_item(value: "40") { "Option 40" } %>
  <% select.with_item(value: "41") { "Option 41" } %>
  <% select.with_item(value: "42") { "Option 42" } %>
  <% select.with_item(value: "43") { "Option 43" } %>
  <% select.with_item(value: "44") { "Option 44" } %>
  <% select.with_item(value: "45") { "Option 45" } %>
  <% select.with_item(value: "46") { "Option 46" } %>
  <% select.with_item(value: "47") { "Option 47" } %>
  <% select.with_item(value: "48") { "Option 48" } %>
  <% select.with_item(value: "49") { "Option 49" } %>
  <% select.with_item(value: "50") { "Option 50" } %>
<% end %>

In Form Layout

select/in_form_layout
<%= render(Shadcn::SelectComponent.new(name: "role", placeholder: "Select a role")) do |select| %>
  <% select.with_item(value: "developer") { "Developer" } %>
  <% select.with_item(value: "designer") { "Designer" } %>
  <% select.with_item(value: "manager") { "Manager" } %>
  <% select.with_item(value: "owner") { "Owner" } %>
<% end %>

API Reference

SelectComponent

Prop Type Default Description
name String nil Form field name for the hidden input
id String nil Element ID for the hidden input
value String nil Currently selected value
placeholder String "Select..." Placeholder text shown when no value is selected
disabled Boolean false Whether the select is disabled
required Boolean false Whether the select is required for form validation

Slots

Slot Props Description
with_item value:, disabled: A selectable option (yields content)
with_group label: A group of options with an optional label (yields SelectGroupComponent)

SelectItemComponent

Prop Type Default Description
value String required The value submitted when this option is selected
disabled Boolean false Whether this option is disabled

SelectGroupComponent

Prop Type Default Description
label String nil Optional label for the group

The SelectGroupComponent also accepts with_item slots for items within the group.

Stimulus Controller

This component requires JavaScript. The Stimulus controller shadcn--select provides interactivity.

Manages select dropdown state, keyboard navigation, value selection, and accessibility.

Installation

Add to your config/importmap.rb:

pin "shadcn", to: "index.esm.js"

Then in your app/javascript/controllers/index.js:

import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn"

const application = Application.start()
registerShadcnControllers(application)

Or import just this controller:

import SelectController from "shadcn-rails-stimulus/controllers/select_controller"

application.register("shadcn--select", SelectController)

Targets

Name Description
trigger The button that toggles the dropdown
content The dropdown content container
input The hidden input that stores the selected value
item Each selectable option in the dropdown
display The text display showing the current selection
checkIcon The checkmark icon for selected items

Values

Name Type Default Description
value String "" Currently selected value

Actions

Action Description
toggle Opens or closes the dropdown
open Opens the dropdown
close Closes the dropdown
select Selects an item from the dropdown
handleKeydown Handles keyboard navigation (Arrow keys, Enter, Escape, Home, End)

TypeScript

Type definitions are included. Import types as needed:

import type { SelectController } from "shadcn-rails-stimulus"

Accessibility

  • Uses role="combobox" on the trigger for proper screen reader announcement
  • Uses role="listbox" for the dropdown content
  • Uses role="option" for each selectable item
  • Sets aria-expanded to indicate dropdown open/closed state
  • Sets aria-selected to indicate selected option
  • Supports full keyboard navigation:
    • Space or Enter to open/select
    • Arrow Down / Arrow Up to navigate options
    • Home / End to jump to first/last option
    • Escape to close dropdown
  • Disabled items are properly marked and skipped during keyboard navigation
  • Focus returns to trigger after selection or closing
  • Visual checkmark indicator for the selected option