We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Combobox
A fully accessible searchable input with intelligent positioning, keyboard navigation, and flexible data handling. Perfect for autocomplete scenarios, large option sets, and user-generated content.
The combobox combines text input with dropdown suggestions, supporting both frontend filtering and server-side async search. Features full keyboard navigation, smart positioning with Floating UI, and seamless Phoenix form integration.
Quick Start
The most basic combobox requires three components: .combobox, .combobox_input, and
.combobox_options
with .combobox_option
elements.
<div class="max-w-sm">
<label class="block text-sm font-medium text-gray-700 mb-2">Programming Language</label>
<.combobox class="w-64" id="basic-combobox">
<.combobox_input
name="language"
class="block w-full rounded-md border-0 py-2 pl-3 pr-10 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder="Search languages..."
/>
<.combobox_options
id="basic-combobox-options"
offset={4}
class="max-h-60 w-64 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-gray-300 focus:outline-none sm:text-sm z-50"
>
<%= for language <- [
"JavaScript", "TypeScript", "Python", "Ruby", "Go", "Rust", "Java", "C++",
"C#", "PHP", "Swift", "Kotlin", "Dart", "Elixir", "Haskell", "Scala",
"Clojure", "F#", "OCaml", "Erlang", "Crystal", "Nim", "Zig", "V"
] do %>
<.combobox_option
value={language}
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
{language}
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-selected:flex group-data-focus:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<% end %>
</.combobox_options>
</.combobox>
</div>
Start typing to filter through options. The component automatically handles frontend filtering and keyboard navigation. Use arrow keys to navigate and Enter to select.
Advanced Usage
Server-side Search
For large datasets or dynamic content, enable server-side search by adding
phx-change
to the input.
The component automatically switches to async mode with debounced search queries.
defmodule PrimaWeb.DemoLive.AsyncComboboxDemo do
@moduledoc false
use PrimaWeb, :live_component
import Prima.Combobox
@options [
"Cherry",
"Kiwi",
"Grapefruit",
"Orange",
"Banana"
]
@impl true
def mount(socket) do
socket =
socket
|> stream_configure(:suggestions, dom_id: &"suggestions-#{&1}")
|> stream(:suggestions, [])
{:ok, socket}
end
@impl true
def render(assigns) do
~H"""
<form phx-submit="save">
<.combobox class="w-64" id="demo-async-combobox">
<div class="relative mt-2 rounded-md shadow-sm">
<.combobox_input
name="user[favourite_fruit]"
class="block w-full rounded-md border-0 py-2 pl-3 pr-10 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 peer"
phx-change="async_combobox_search"
phx-target={@myself}
placeholder="Type to search..."
/>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3 invisible peer-[.phx-change-loading]:visible">
<svg
class="animate-spin -ml-1 h-5 w-5 text-gray-500"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4">
</circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
>
</path>
</svg>
</div>
</div>
<.combobox_options
class="max-h-60 w-64 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-gray-200 focus:outline-none sm:text-sm z-50"
phx-update="stream"
id="demo-async-combobox-options"
offset={4}
>
<.combobox_option
:for={{dom_id, option} <- @streams.suggestions}
id={dom_id}
value={option}
class="relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
{option}
</.combobox_option>
</.combobox_options>
</.combobox>
</form>
"""
end
@impl true
def handle_event("async_combobox_search", params, socket) do
input = get_in(params, params["_target"])
suggestions =
Enum.filter(@options, fn option ->
String.contains?(String.downcase(option), String.downcase(input))
end)
{:noreply, stream(socket, :suggestions, suggestions, reset: true)}
end
end
The async pattern sends search queries to your LiveView with automatic debouncing. Handle the
phx-change
event to filter results server-side and update the options dynamically.
Create Custom Values
Allow users to create values that don't exist in the predefined list using the
.creatable_option
component.
Perfect for tags, categories, or any user-generated content scenarios.
Type a custom tag name and press Enter to create a new one.
<div class="max-w-sm">
<label class="block text-sm font-medium text-gray-700 mb-2">Project Tags</label>
<.combobox class="w-64" id="creatable-combobox">
<.combobox_input
name="tag"
class="block w-full rounded-md border-0 py-2 pl-3 pr-10 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder="Search or create tags..."
/>
<.combobox_options
id="creatable-combobox-options"
offset={4}
class="max-h-60 w-64 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-gray-200 focus:outline-none sm:text-sm z-50"
>
<%= for tag <- ["frontend", "backend", "database", "api", "testing", "performance", "security", "ui-ux"] do %>
<.combobox_option
value={tag}
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800 group-data-[focus=true]:bg-indigo-700 group-data-[focus=true]:text-white">
{tag}
</span>
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<% end %>
<.creatable_option class="group relative cursor-default select-none py-2 pl-3 pr-9 text-indigo-600 data-focus:bg-indigo-600 data-focus:text-white border-t border-gray-100 italic" />
</.combobox_options>
</.combobox>
<p class="mt-2 text-xs text-gray-500">
Type a custom tag name and press Enter to create a new one.
</p>
</div>
The creatable option automatically appears when the user types something that doesn't match existing options. It shows "Create '[user input]'" and submits the typed value when selected.
Multiple Selection
Enable multiple selection by adding the multiple={true}
attribute to the combobox. Use the .combobox_selections
component to display selected items as removable pills.
- __VALUE__
Select multiple skills. Click the × button or press Backspace to remove selections.
<div class="max-w-sm">
<label class="block text-sm font-medium text-gray-700 mb-2">Select Skills</label>
<.combobox class="w-72" id="multi-select-demo" multiple={true}>
<div
id="multi-select-demo-field"
class="flex flex-wrap gap-1.5 items-center min-h-[42px] w-full rounded-md border-0 py-1.5 px-3 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600"
>
<.combobox_selections>
<:selection
:let={value}
class="inline-flex items-center gap-1 px-2 py-0.5 bg-indigo-100 text-indigo-800 rounded text-sm font-medium"
>
<span>{value}</span>
<.combobox_selection_remove
value={value}
class="hover:bg-indigo-200 rounded-full p-0.5 transition-colors"
>
<svg class="size-3" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
</.combobox_selection_remove>
</:selection>
</.combobox_selections>
<.combobox_input
name="skills"
class="flex-1 min-w-[100px] border-0 p-0 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6 bg-transparent outline-none"
placeholder="Search skills..."
/>
</div>
<.combobox_options
id="multi-select-demo-options"
reference="#multi-select-demo-field"
offset={4}
class="max-h-60 w-72 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-gray-200 focus:outline-none sm:text-sm z-50"
>
<%= for skill <- [
"React", "Vue", "Angular", "Svelte",
"TypeScript", "JavaScript", "Python", "Elixir",
"PostgreSQL", "MongoDB", "Redis", "GraphQL",
"Docker", "Kubernetes", "AWS", "Tailwind CSS"
] do %>
<.combobox_option
value={skill}
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white data-selected:font-semibold"
>
{skill}
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<% end %>
</.combobox_options>
</.combobox>
<p class="mt-2 text-xs text-gray-500">
Select multiple skills. Click the × button or press Backspace to remove selections.
</p>
</div>
The .combobox_selections
component uses a template-based approach for rendering pills. Customize the appearance using the
:selection
slot - it supports any markup including icons, badges, and remove buttons. The dropdown stays open after each selection for quick multi-select workflows, and selected items are visually highlighted with data-selected="true".
For a react-select style appearance, wrap the selections and input in a flex container styled to look like an input field. The pills and input appear together in the same visual container, creating a seamless multi-select experience.
Smart Positioning
Powered by Floating UI, the combobox intelligently positions itself using the placement
and offset
attributes.
It automatically flips to the opposite side when there's insufficient space.
<div class="max-w-sm space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Fruit (opens above)</label>
<.combobox class="w-64" id="top-combobox">
<.combobox_input
name="top_fruit"
class="block w-full rounded-md border-0 py-2 pl-3 pr-10 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder="Choose fruit..."
/>
<.combobox_options
id="top-combobox-options"
placement="top-start"
offset={4}
class="max-h-32 w-64 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-gray-200 focus:outline-none sm:text-sm z-50"
transition_enter={
{"ease-out duration-100", "transform opacity-0 scale-95",
"transform opacity-100 scale-100"}
}
transition_leave={
{"transition ease-in duration-75", "transform opacity-100 scale-100",
"transform opacity-0 scale-95"}
}
>
<%= for option <- ["Apple", "Banana", "Cherry", "Date"] do %>
<.combobox_option
value={option}
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
{option}
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<% end %>
</.combobox_options>
</.combobox>
</div>
<!-- Right Placement -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Color (opens to right)</label>
<.combobox class="w-64" id="right-combobox">
<.combobox_input
name="right_color"
class="block w-full rounded-md border-0 py-2 pl-3 pr-10 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder="Choose color..."
/>
<.combobox_options
id="right-combobox-options"
placement="right-start"
offset={8}
class="max-h-48 w-32 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-gray-200 focus:outline-none sm:text-sm z-50"
transition_enter={
{"ease-out duration-100", "transform opacity-0 scale-95",
"transform opacity-100 scale-100"}
}
transition_leave={
{"transition ease-in duration-75", "transform opacity-100 scale-100",
"transform opacity-0 scale-95"}
}
>
<%= for color <- ["Red", "Blue", "Green", "Yellow", "Purple", "Orange"] do %>
<.combobox_option
value={color}
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
{color}
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<% end %>
</.combobox_options>
</.combobox>
</div>
<!-- Bottom with Large Offset -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Country (12px offset)</label>
<.combobox class="w-64" id="offset-combobox">
<.combobox_input
name="offset_country"
class="block w-full rounded-md border-0 py-2 pl-3 pr-10 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder="Select country..."
/>
<.combobox_options
id="offset-combobox-options"
placement="bottom-start"
offset={12}
class="max-h-48 w-64 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-gray-200 focus:outline-none sm:text-sm z-50"
transition_enter={
{"ease-out duration-100", "transform opacity-0 scale-95",
"transform opacity-100 scale-100"}
}
transition_leave={
{"transition ease-in duration-75", "transform opacity-100 scale-100",
"transform opacity-0 scale-95"}
}
>
<%= for {country, flag} <- [
{"United States", "🇺🇸"}, {"Canada", "🇨🇦"}, {"United Kingdom", "🇬🇧"},
{"Germany", "🇩🇪"}, {"France", "🇫🇷"}, {"Japan", "🇯🇵"}, {"Australia", "🇦🇺"}
] do %>
<.combobox_option
value={country}
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
<span class="mr-2"><%= flag %></span>{country}
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<% end %>
</.combobox_options>
</.combobox>
</div>
</div>
Use placement="top-start", placement="right-start", or other positioning options.
The component automatically repositions on scroll and window resize to stay visible.
Flexible Markup
Combobox options support rich, custom markup including icons, descriptions, badges, and complex layouts. The component is completely unstyled, giving you full control over the visual presentation.
<div class="max-w-sm">
<label class="block text-sm font-medium text-gray-700 mb-2">Task Priority</label>
<.combobox class="w-64" id="flexible-markup-combobox">
<.combobox_input
name="priority"
class="block w-full rounded-md border-0 py-2 pl-3 pr-10 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder="Select priority..."
/>
<.combobox_options
id="flexible-markup-options"
offset={4}
class="max-h-60 w-64 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-gray-200 focus:outline-none sm:text-sm z-50"
>
<.combobox_option
value="urgent"
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
<div class="flex items-center">
<svg
class="w-4 h-4 text-red-500 mr-3 group-data-[focus]:text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"
/>
</svg>
<div>
<div class="font-medium group-data-[selected=true]:font-bold">Urgent</div>
<div class="text-xs text-gray-500 group-data-[focus]:text-indigo-200">
Needs immediate attention
</div>
</div>
</div>
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<.combobox_option
value="high"
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
<div class="flex items-center">
<svg
class="w-4 h-4 text-orange-500 mr-3 group-data-[focus]:text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-8.293l-3-3a1 1 0 00-1.414 0l-3 3a1 1 0 001.414 1.414L9 9.414V13a1 1 0 102 0V9.414l1.293 1.293a1 1 0 001.414-1.414z"
clip-rule="evenodd"
/>
</svg>
<div>
<div class="font-medium group-data-[selected=true]:font-bold">High Priority</div>
<div class="text-xs text-gray-500 group-data-[focus]:text-indigo-200">
Important task
</div>
</div>
</div>
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<.combobox_option
value="medium"
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
<div class="flex items-center">
<svg
class="w-4 h-4 text-yellow-500 mr-3 group-data-[focus]:text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z"
clip-rule="evenodd"
/>
</svg>
<div>
<div class="font-medium group-data-[selected=true]:font-bold">Medium Priority</div>
<div class="text-xs text-gray-500 group-data-[focus]:text-indigo-200">
Standard timeline
</div>
</div>
</div>
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<.combobox_option
value="low"
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
<div class="flex items-center">
<svg
class="w-4 h-4 text-green-500 mr-3 group-data-[focus]:text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-8.293l-3-3a1 1 0 00-1.414 0l-3 3a1 1 0 001.414 1.414L9 9.414V13a1 1 0 102 0V9.414l1.293 1.293a1 1 0 001.414-1.414z"
clip-rule="evenodd"
transform="rotate(180 10 10)"
/>
</svg>
<div>
<div class="font-medium group-data-[selected=true]:font-bold">Low Priority</div>
<div class="text-xs text-gray-500 group-data-[focus]:text-indigo-200">
When time permits
</div>
</div>
</div>
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
<.combobox_option
value="backlog"
class="group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 data-focus:bg-indigo-600 data-focus:text-white"
>
<div class="flex items-center">
<svg
class="w-4 h-4 text-gray-400 mr-3 group-data-[focus]:text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
clip-rule="evenodd"
/>
</svg>
<div>
<div class="font-medium group-data-[selected=true]:font-bold">Backlog</div>
<div class="text-xs text-gray-500 group-data-[focus]:text-indigo-200">
Future consideration
</div>
</div>
</div>
<span class="hidden absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-data-[selected=true]:flex group-data-[focus=true]:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</.combobox_option>
</.combobox_options>
</.combobox>
</div>
Build complex option layouts with multiple text elements, icons, images, or any HTML content.
Use group-data-focus:text-white
and similar selectors to style elements based on focus state.
Styling States
Combobox options expose two data attributes for styling different interaction states:
-
data-focus- Set totruewhen an option is focused via keyboard navigation or mouse hover. Perfect for highlighting the currently focused item. -
data-selected- Set totrueon the option that matches the current selection. Use this to visually indicate which option is selected, even when reopening the dropdown.
Use Tailwind's data-focus:
and data-selected:
modifiers to style these states.
Keyboard Interaction
The combobox component provides full keyboard accessibility with comprehensive navigation support. All interactions follow standard ARIA patterns for combobox components.
| Key | Description |
|---|---|
| When the input is focused: | |
| ↓ / ↑ | Opens the combobox and focuses the first option |
| When the combobox is open: | |
| Esc | Closes the combobox and restores the previously selected value |
| ↑ / ↓ | Focuses the previous/next option (wraps around) |
| Home / PageUp | Focuses the first option |
| End / PageDown | Focuses the last option |
| Enter | Selects the currently focused option and closes the combobox |
| Tab | Selects the currently focused option and closes the combobox |
| A-Z / a-z | Filters available options (frontend mode) or triggers search event (async mode) |
| Backspace | Removes the last selected item (multi-select mode, when input is empty) |