Native Select
A styled native HTML select element with consistent design system integration.
Usage
Default
div(class: "grid w-full max-w-sm items-center gap-1.5") do NativeSelect do NativeSelectOption(value: "") { "Select a fruit" } NativeSelectOption(value: "apple") { "Apple" } NativeSelectOption(value: "banana") { "Banana" } NativeSelectOption(value: "blueberry") { "Blueberry" } NativeSelectOption(value: "pineapple") { "Pineapple" } end end
Groups
Use NativeSelectGroup to organize options into categories.
div(class: "grid w-full max-w-sm items-center gap-1.5") do NativeSelect do NativeSelectOption(value: "") { "Select a department" } NativeSelectGroup(label: "Engineering") do NativeSelectOption(value: "frontend") { "Frontend" } NativeSelectOption(value: "backend") { "Backend" } NativeSelectOption(value: "devops") { "DevOps" } end NativeSelectGroup(label: "Sales") do NativeSelectOption(value: "account_executive") { "Account Executive" } NativeSelectOption(value: "sales_development") { "Sales Development" } end end end
Disabled
Add the disabled attribute to the NativeSelect component to disable the select.
div(class: "grid w-full max-w-sm items-center gap-1.5") do NativeSelect(disabled: true) do NativeSelectOption(value: "") { "Select a fruit" } NativeSelectOption(value: "apple") { "Apple" } NativeSelectOption(value: "banana") { "Banana" } NativeSelectOption(value: "blueberry") { "Blueberry" } end end
Invalid
Use aria-invalid to show validation errors.
div(class: "grid w-full max-w-sm items-center gap-1.5") do NativeSelect(aria: {invalid: "true"}) do NativeSelectOption(value: "") { "Select a fruit" } NativeSelectOption(value: "apple") { "Apple" } NativeSelectOption(value: "banana") { "Banana" } NativeSelectOption(value: "blueberry") { "Blueberry" } end end
Native Select vs Select
NativeSelect: Choose for native browser behavior, superior performance, or mobile-optimized dropdowns.
Select: Choose for custom styling, animations, or complex interactions.
Installation
Using RubyUI CLI
Run the install command
rails g ruby_ui:component NativeSelect
Manual installation
1
Add RubyUI::NativeSelect to app/components/ruby_ui/native_select/native_select.rb
# frozen_string_literal: true module RubyUI class NativeSelect < Base def initialize(size: :default, **attrs) @size = size super(**attrs) end def view_template(&block) div( class: "group/native-select relative w-fit has-[select:disabled]:opacity-50" ) do select(**attrs, &block) render RubyUI::NativeSelectIcon.new end end private def default_attrs { data: { ruby_ui__form_field_target: "input", action: "change->ruby-ui--form-field#onChange invalid->ruby-ui--form-field#onInvalid" }, class: [ "border-border bg-transparent text-sm w-full min-w-0 appearance-none rounded-md border py-1 pr-8 pl-2.5 shadow-xs transition-[color,box-shadow] outline-none select-none ring-0 ring-ring/0", "placeholder:text-muted-foreground", "selection:bg-primary selection:text-primary-foreground", "focus-visible:outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-2", "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", "aria-invalid:ring-destructive/20 aria-invalid:border-destructive aria-invalid:ring-2", (@size == :sm) ? "h-7 rounded-md py-0.5" : "h-9" ] } end end end
2
Add RubyUI::NativeSelectDocs to app/components/ruby_ui/native_select/native_select_docs.rb
# frozen_string_literal: true class Views::Docs::NativeSelect < Views::Base def view_template component = "NativeSelect" div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do render Docs::Header.new(title: "Native Select", description: "A styled native HTML select element with consistent design system integration.") Heading(level: 2) { "Usage" } render Docs::VisualCodeExample.new(title: "Default", context: self) do <<~RUBY div(class: "grid w-full max-w-sm items-center gap-1.5") do NativeSelect do NativeSelectOption(value: "") { "Select a fruit" } NativeSelectOption(value: "apple") { "Apple" } NativeSelectOption(value: "banana") { "Banana" } NativeSelectOption(value: "blueberry") { "Blueberry" } NativeSelectOption(value: "pineapple") { "Pineapple" } end end RUBY end render Docs::VisualCodeExample.new(title: "Groups", description: "Use NativeSelectGroup to organize options into categories.", context: self) do <<~RUBY div(class: "grid w-full max-w-sm items-center gap-1.5") do NativeSelect do NativeSelectOption(value: "") { "Select a department" } NativeSelectGroup(label: "Engineering") do NativeSelectOption(value: "frontend") { "Frontend" } NativeSelectOption(value: "backend") { "Backend" } NativeSelectOption(value: "devops") { "DevOps" } end NativeSelectGroup(label: "Sales") do NativeSelectOption(value: "account_executive") { "Account Executive" } NativeSelectOption(value: "sales_development") { "Sales Development" } end end end RUBY end render Docs::VisualCodeExample.new(title: "Disabled", description: "Add the disabled attribute to the NativeSelect component to disable the select.", context: self) do <<~RUBY div(class: "grid w-full max-w-sm items-center gap-1.5") do NativeSelect(disabled: true) do NativeSelectOption(value: "") { "Select a fruit" } NativeSelectOption(value: "apple") { "Apple" } NativeSelectOption(value: "banana") { "Banana" } NativeSelectOption(value: "blueberry") { "Blueberry" } end end RUBY end render Docs::VisualCodeExample.new(title: "Invalid", description: "Use aria-invalid to show validation errors.", context: self) do <<~RUBY div(class: "grid w-full max-w-sm items-center gap-1.5") do NativeSelect(aria: {invalid: "true"}) do NativeSelectOption(value: "") { "Select a fruit" } NativeSelectOption(value: "apple") { "Apple" } NativeSelectOption(value: "banana") { "Banana" } NativeSelectOption(value: "blueberry") { "Blueberry" } end end RUBY end Heading(level: 2) { "Native Select vs Select" } div(class: "space-y-2 text-sm text-muted-foreground") do p { "NativeSelect: Choose for native browser behavior, superior performance, or mobile-optimized dropdowns." } p { "Select: Choose for custom styling, animations, or complex interactions." } end render Components::ComponentSetup::Tabs.new(component_name: component) render Docs::ComponentsTable.new(component_files(component)) end end end
3
Add RubyUI::NativeSelectGroup to app/components/ruby_ui/native_select/native_select_group.rb
# frozen_string_literal: true module RubyUI class NativeSelectGroup < Base def view_template(&) optgroup(**attrs, &) end private def default_attrs {} end end end
4
Add RubyUI::NativeSelectIcon to app/components/ruby_ui/native_select/native_select_icon.rb
# frozen_string_literal: true module RubyUI class NativeSelectIcon < Base def view_template(&block) span(**attrs) do if block block.call else icon end end end private def icon svg( xmlns: "http://www.w3.org/2000/svg", viewbox: "0 0 24 24", fill: "none", stroke: "currentColor", stroke_width: "2", stroke_linecap: "round", stroke_linejoin: "round", class: "size-4", aria_hidden: "true" ) do |s| s.path(d: "m6 9 6 6 6-6") end end def default_attrs { class: "text-muted-foreground pointer-events-none absolute top-1/2 right-2.5 -translate-y-1/2 select-none" } end end end
5
Add RubyUI::NativeSelectOption to app/components/ruby_ui/native_select/native_select_option.rb
# frozen_string_literal: true module RubyUI class NativeSelectOption < Base def view_template(&) option(**attrs, &) end private def default_attrs {} end end end
Components
| Component | Built using | Source |
|---|---|---|
NativeSelect | Phlex | |
NativeSelectDocs | Phlex | |
NativeSelectGroup | Phlex | |
NativeSelectIcon | Phlex | |
NativeSelectOption | Phlex |