Combobox

Autocomplete input with a searchable dropdown of suggestions. Supports single and multi-select with chips.

Quick Start

<%= kui(:combobox, name: :framework) do %>
  <%= kui(:combobox, :input, placeholder: "Select a framework") %>
  <%= kui(:combobox, :content) do %>
    <%= kui(:combobox, :list) do %>
      <%= kui(:combobox, :item, value: "rails") { "Rails" } %>
      <%= kui(:combobox, :item, value: "django") { "Django" } %>
      <%= kui(:combobox, :item, value: "laravel") { "Laravel" } %>
      <%= kui(:combobox, :empty) { "No frameworks found." } %>
    <% end %>
  <% end %>
<% end %>

Locals

Combobox (root)

Local Type Default
name: Symbol | String nil
multiple: Boolean false
css_classes: String ""
**component_options Hash {}

ComboboxInput

Local Type Default
placeholder: String nil
disabled: Boolean false
css_classes: String ""
**component_options Hash {}

ComboboxItem

Local Type Default
value: String (required)
disabled: Boolean false
css_classes: String ""
**component_options Hash {}

ComboboxChip

Local Type Default
value: String (required)
removable: Boolean true
css_classes: String ""
**component_options Hash {}

ComboboxChipsInput

Local Type Default
placeholder: String nil
disabled: Boolean false
css_classes: String ""
**component_options Hash {}

Sub-parts

Part Usage Purpose
:input kui(:combobox, :input) Text input with trigger chevron
:content kui(:combobox, :content) Dropdown panel
:list kui(:combobox, :list) Scrollable list inside the dropdown
:item kui(:combobox, :item) Selectable option with checkmark
:empty kui(:combobox, :empty) Shown when no items match the filter
:group kui(:combobox, :group) Groups related items
:label kui(:combobox, :label) Group heading label
:separator kui(:combobox, :separator) Divider between groups
:chips kui(:combobox, :chips) Multi-select chip container
:chip kui(:combobox, :chip) Individual selected chip
:chips_input kui(:combobox, :chips_input) Inline text input inside chips container

All sub-parts accept css_classes: and **component_options.

Anatomy

Combobox
├── ComboboxInput (single select)
│   ├── Text input
│   └── Chevron trigger
├── -OR- ComboboxChips (multi-select)
│   ├── ComboboxChip (per selected value)
│   │   ├── Chip text
│   │   └── Remove button (x icon)
│   └── ComboboxChipsInput
├── ComboboxContent (dropdown)
│   └── ComboboxList (listbox)
│       ├── ComboboxGroup
│       │   ├── ComboboxLabel
│       │   └── ComboboxItem
│       │       ├── Item text
│       │       └── Checkmark indicator
│       ├── ComboboxSeparator
│       ├── ComboboxItem (ungrouped)
│       └── ComboboxEmpty
└── Hidden input (form submission)

Usage

Single Select

The default mode. The input field acts as both a search filter and display for the selected value.

<%= kui(:combobox, name: :framework) do %>
  <%= kui(:combobox, :input, placeholder: "Select a framework") %>
  <%= kui(:combobox, :content) do %>
    <%= kui(:combobox, :list) do %>
      <%= kui(:combobox, :item, value: "rails") { "Rails" } %>
      <%= kui(:combobox, :item, value: "django") { "Django" } %>
      <%= kui(:combobox, :item, value: "laravel") { "Laravel" } %>
      <%= kui(:combobox, :empty) { "No frameworks found." } %>
    <% end %>
  <% end %>
<% end %>

Multi-select with Chips

Set multiple: true and use the chips sub-parts instead of the input.

<%= kui(:combobox, name: "tags[]", multiple: true) do %>
  <%= kui(:combobox, :chips) do %>
    <%= kui(:combobox, :chip, value: "rails") { "Rails" } %>
    <%= kui(:combobox, :chips_input, placeholder: "Add tag...") %>
  <% end %>
  <%= kui(:combobox, :content) do %>
    <%= kui(:combobox, :list) do %>
      <%= kui(:combobox, :item, value: "rails") { "Rails" } %>
      <%= kui(:combobox, :item, value: "django") { "Django" } %>
      <%= kui(:combobox, :item, value: "laravel") { "Laravel" } %>
      <%= kui(:combobox, :empty) { "No tags found." } %>
    <% end %>
  <% end %>
<% end %>

Groups

Use ComboboxGroup, ComboboxLabel, and ComboboxSeparator to organize items.

<%= kui(:combobox, name: :timezone) do %>
  <%= kui(:combobox, :input, placeholder: "Select a timezone") %>
  <%= kui(:combobox, :content) do %>
    <%= kui(:combobox, :list) do %>
      <%= kui(:combobox, :group) do %>
        <%= kui(:combobox, :label) { "Americas" } %>
        <%= kui(:combobox, :item, value: "new-york") { "(GMT-5) New York" } %>
        <%= kui(:combobox, :item, value: "los-angeles") { "(GMT-8) Los Angeles" } %>
      <% end %>
      <%= kui(:combobox, :separator) %>
      <%= kui(:combobox, :group) do %>
        <%= kui(:combobox, :label) { "Europe" } %>
        <%= kui(:combobox, :item, value: "london") { "(GMT+0) London" } %>
        <%= kui(:combobox, :item, value: "paris") { "(GMT+1) Paris" } %>
      <% end %>
      <%= kui(:combobox, :empty) { "No timezones found." } %>
    <% end %>
  <% end %>
<% end %>

Disabled

Disable the input to prevent interaction.

<%= kui(:combobox, :input, placeholder: "Select a framework", disabled: true) %>

With Field

Wrap in a Field for label, description, and error support.

<%= kui(:field) do %>
  <%= kui(:field, :label, for: :framework) { "Framework" } %>
  <%= kui(:combobox, name: :framework) do %>
    <%= kui(:combobox, :input, placeholder: "Search frameworks...") %>
    <%= kui(:combobox, :content) do %>
      <%= kui(:combobox, :list) do %>
        <%= kui(:combobox, :item, value: "rails") { "Rails" } %>
        <%= kui(:combobox, :item, value: "django") { "Django" } %>
      <% end %>
    <% end %>
  <% end %>
  <%= kui(:field, :description) { "Choose your preferred framework." } %>
<% end %>

Theme

# lib/kiso/themes/combobox.rb (abbreviated)
ComboboxInput = ClassVariants.build(
  base: "text-foreground flex w-full items-center rounded-md
         bg-background ring ring-inset ring-accented shadow-xs h-9 ..."
)

ComboboxContent = ClassVariants.build(
  base: "bg-background text-foreground z-50 max-h-96 min-w-32
         overflow-hidden rounded-md shadow-md ring ring-inset ring-border"
)

ComboboxItem = ClassVariants.build(
  base: "relative flex w-full cursor-default items-center gap-2 rounded-sm
         py-1.5 pr-8 pl-2 text-sm outline-none select-none
         data-[highlighted]:bg-elevated data-[highlighted]:text-foreground"
)

ComboboxChips = ClassVariants.build(
  base: "flex min-h-9 flex-wrap items-center gap-1.5 rounded-md
         bg-background ring ring-inset ring-accented shadow-xs ..."
)

ComboboxChip = ClassVariants.build(
  base: "bg-muted text-foreground flex h-5.5 w-fit items-center
         justify-center gap-1 rounded-sm px-1.5 text-xs font-medium ..."
)

Stimulus Controller

The Combobox component requires the kiso--combobox Stimulus controller for interactivity. It handles:

  • Filter items as the user types in the input
  • Keyboard navigation (Arrow keys, Home, End)
  • Enter to select highlighted item
  • Escape to close the dropdown
  • Backspace to remove last chip (multi-select)
  • Click outside to dismiss
  • Hidden input sync for form submission
  • Checkmark indicators on selected items
  • Chip creation/removal for multi-select
  • Group/separator visibility during filtering

Register the controller in your application:

import KisoUi from "kiso-ui"
KisoUi.start(application)

Accessibility

Attribute Value
role="listbox" On the list
role="option" On each item
role="group" On each group
aria-selected true on selected items
aria-disabled On disabled items
data-slot "combobox", "combobox-input", etc.

Keyboard

Key Action
ArrowDown / ArrowUp Navigate through visible items
Enter Select highlighted item
Escape Close the dropdown
Home / End Jump to first / last item
Backspace Remove last chip (multi-select, empty input)
Tab Close and move focus
Type Filter items by text