Combobox

A Tailwind CSS combobox component that combines a trigger button with a searchable, keyboard-navigable list. Supports single and multi-select.

Next.js
SvelteKit
Nuxt.js
Remix
Astro
No framework found.
<button
  class="combobox-trigger w-56"
  type="button"
  data-sp-toggle="combobox"
  data-sp-target="#combobox-framework"
  aria-expanded="false"
>
  <span class="combobox-value" data-sp-placeholder="Select a framework..."></span>
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
</button>
<div id="combobox-framework" class="combobox" role="listbox">
  <div class="combobox-search">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
    <input class="combobox-input" type="text" placeholder="Search framework..." />
  </div>
  <div class="combobox-list">
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="framework" value="next" />
      Next.js
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="framework" value="sveltekit" />
      SvelteKit
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="framework" value="nuxt" />
      Nuxt.js
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="framework" value="remix" />
      Remix
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="framework" value="astro" />
      Astro
    </div>
  </div>
  <div class="combobox-empty">No framework found.</div>
</div>

With label

Pair with a Label and wrap in a Field for accessible form fields. Use aria-labelledby on the trigger pointing to the label's id so screen readers announce it correctly.

Next.js
SvelteKit
Nuxt.js
Remix
Astro
No framework found.
<div class="w-fit">
  <div class="field">
    <label class="label" id="framework-label">Framework</label>
    <button
      class="combobox-trigger w-56"
      type="button"
      data-sp-toggle="combobox"
      data-sp-target="#combobox-labelled"
      aria-expanded="false"
      aria-labelledby="framework-label"
    >
      <span class="combobox-value" data-sp-placeholder="Select a framework..."></span>
      <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
    </button>
    <div id="combobox-labelled" class="combobox" role="listbox">
      <div class="combobox-search">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
        <input class="combobox-input" type="text" placeholder="Search framework..." />
      </div>
      <div class="combobox-list">
        <div class="combobox-item" role="option" tabindex="0">
          <input type="radio" class="sr-only" tabindex="-1" name="framework-labelled" value="next" />
          Next.js
        </div>
        <div class="combobox-item" role="option" tabindex="0">
          <input type="radio" class="sr-only" tabindex="-1" name="framework-labelled" value="sveltekit" />
          SvelteKit
        </div>
        <div class="combobox-item" role="option" tabindex="0">
          <input type="radio" class="sr-only" tabindex="-1" name="framework-labelled" value="nuxt" />
          Nuxt.js
        </div>
        <div class="combobox-item" role="option" tabindex="0">
          <input type="radio" class="sr-only" tabindex="-1" name="framework-labelled" value="remix" />
          Remix
        </div>
        <div class="combobox-item" role="option" tabindex="0">
          <input type="radio" class="sr-only" tabindex="-1" name="framework-labelled" value="astro" />
          Astro
        </div>
      </div>
      <div class="combobox-empty">No framework found.</div>
    </div>
  </div>
</div>

Single select

Use <input type="radio"> inside each item for single select. Clicking an item checks the radio, updates the trigger text, and closes the menu. Only one item can be selected at a time.

Next.js
SvelteKit
Nuxt.js
Remix
Astro
No framework found.
<button
  class="combobox-trigger w-56"
  type="button"
  data-sp-toggle="combobox"
  data-sp-target="#combobox-single"
  aria-expanded="false"
>
  <span class="combobox-value" data-sp-placeholder="Select a framework..."></span>
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
</button>
<div id="combobox-single" class="combobox" role="listbox">
  <div class="combobox-search">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
    <input class="combobox-input" type="text" placeholder="Search framework..." />
  </div>
  <div class="combobox-list">
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="single-framework" value="next" />
      Next.js
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="single-framework" value="sveltekit" />
      SvelteKit
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="single-framework" value="nuxt" />
      Nuxt.js
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="single-framework" value="remix" />
      Remix
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="single-framework" value="astro" />
      Astro
    </div>
  </div>
  <div class="combobox-empty">No framework found.</div>
</div>

Multi-select

Use <input type="checkbox"> inside each item for multi-select. The menu stays open after selection. When two or more items are selected, the trigger collapses to "N selected" automatically.

ESLint
Prettier
TypeScript
Vitest
No tools found.
<button
  class="combobox-trigger w-56"
  type="button"
  data-sp-toggle="combobox"
  data-sp-target="#combobox-multi"
  aria-expanded="false"
>
  <span class="combobox-value" data-sp-placeholder="Select tools..."></span>
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
</button>
<div id="combobox-multi" class="combobox" role="listbox">
  <div class="combobox-search">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
    <input class="combobox-input" type="text" placeholder="Search tools..." />
  </div>
  <div class="combobox-list">
    <div class="combobox-item" role="option" tabindex="0">
      <input type="checkbox" class="sr-only" tabindex="-1" name="tools" value="eslint" />
      ESLint
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="checkbox" class="sr-only" tabindex="-1" name="tools" value="prettier" />
      Prettier
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="checkbox" class="sr-only" tabindex="-1" name="tools" value="typescript" />
      TypeScript
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="checkbox" class="sr-only" tabindex="-1" name="tools" value="vitest" />
      Vitest
    </div>
  </div>
  <div class="combobox-empty">No tools found.</div>
</div>

Pre-selected value

Add checked to the input and aria-selected="true" to the item, then render one <span> child per selection inside .combobox-value. The CSS handles single / multi states automatically.

Next.js
SvelteKit
Nuxt.js
Remix
Astro
No framework found.
<button
  class="combobox-trigger w-56"
  type="button"
  data-sp-toggle="combobox"
  data-sp-target="#combobox-preselected"
  aria-expanded="false"
>
  <span class="combobox-value" data-sp-placeholder="Select a framework...">
    <span>SvelteKit</span>
  </span>
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
</button>
<div id="combobox-preselected" class="combobox" role="listbox">
  <div class="combobox-search">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
    <input class="combobox-input" type="text" placeholder="Search framework..." />
  </div>
  <div class="combobox-list">
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="preselected-framework" value="next" />
      Next.js
    </div>
    <div class="combobox-item" role="option" tabindex="0" aria-selected="true">
      <input type="radio" class="sr-only" tabindex="-1" name="preselected-framework" checked="" value="sveltekit" />
      SvelteKit
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="preselected-framework" value="nuxt" />
      Nuxt.js
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="preselected-framework" value="remix" />
      Remix
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="preselected-framework" value="astro" />
      Astro
    </div>
  </div>
  <div class="combobox-empty">No framework found.</div>
</div>

Static trigger

For triggers that should never show selection state (e.g. an action menu disguised as a combobox, or a column-visibility toggle), add data-sp-static to .combobox-value. The placeholder text becomes a permanent label rendered in the default foreground color, and the JS never replaces its content.

Name
Email
Role
<button
  class="combobox-trigger w-32"
  type="button"
  data-sp-toggle="combobox"
  data-sp-target="#combobox-static"
  aria-expanded="false"
>
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 9h18"/><path d="M3 15h18"/><path d="M9 3v18"/><path d="M15 3v18"/></svg>
  <span class="combobox-value" data-sp-placeholder="View" data-sp-static></span>
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
</button>
<div id="combobox-static" class="combobox" role="listbox">
  <div class="combobox-list">
    <div class="combobox-item" role="option" tabindex="0" aria-selected="true">
      <input type="checkbox" class="sr-only" tabindex="-1" name="cols" value="name" checked />
      Name
    </div>
    <div class="combobox-item" role="option" tabindex="0" aria-selected="true">
      <input type="checkbox" class="sr-only" tabindex="-1" name="cols" value="email" checked />
      Email
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="checkbox" class="sr-only" tabindex="-1" name="cols" value="role" />
      Role
    </div>
  </div>
</div>

Submit a form

Combobox triggers can auto-submit their associated <form> so filter toolbars don't need an Apply button. Two trigger conditions are available:

AttributeSubmits when…
data-sp-submit-on-closeThe combobox closes, and selections changed since open (one submit per close)
data-sp-submit-on-changeAny item inside the menu is toggled (one submit per toggle)

Both attributes accept the same optional value: a form id. With no value the closest ancestor <form> is submitted; with a value the matching <form id="..."> is submitted instead.

Don't put both attributes on the same trigger. Pick one trigger condition per combobox — they're meant as alternatives, and combining them produces duplicate submits.

On close

Use data-sp-submit-on-close when the user is likely to make several selections before being done. The form submits once when the menu closes.

<form id="filters" action="/team" method="GET">
  <!-- Inside the form: no value, uses closest ancestor form -->
  <button
    class="combobox-trigger w-44"
    type="button"
    data-sp-toggle="combobox"
    data-sp-target="#combobox-role"
    data-sp-submit-on-close
    aria-expanded="false"
  >
    <span class="combobox-value" data-sp-placeholder="Role"></span>
    <svg>...</svg>
  </button>
  <div id="combobox-role" class="combobox" role="listbox">
    <!-- items with name="roles[]" -->
  </div>
</form>
 
<!-- Outside the form: explicit form id -->
<button
  class="combobox-trigger w-44"
  type="button"
  data-sp-toggle="combobox"
  data-sp-target="#combobox-sort"
  data-sp-submit-on-close="filters"
  aria-expanded="false"
>
  <span class="combobox-value" data-sp-placeholder="Sort"></span>
  <svg>...</svg>
</button>
<div id="combobox-sort" class="combobox" role="listbox">
  <!-- items with name="sort" -->
</div>

On change

Use data-sp-submit-on-change for controls where each toggle should take effect immediately, like a column-visibility picker. The form submits every time an item inside the menu changes.

<form id="filters" action="/team" method="GET">
  <button
    class="combobox-trigger"
    type="button"
    data-sp-toggle="combobox"
    data-sp-target="#combobox-cols"
    data-sp-submit-on-change
    aria-expanded="false"
  >
    <span class="combobox-value" data-sp-placeholder="View" data-sp-static></span>
    <svg>...</svg>
  </button>
  <div id="combobox-cols" class="combobox" role="listbox">
    <!-- items with name="cols[]" -->
  </div>
</form>

With labels

Use .combobox-label and .combobox-separator to group options visually.

Frontend
TypeScript
JavaScript
Backend
Go
Rust
Python
No language found.
<button
  class="combobox-trigger w-56"
  type="button"
  data-sp-toggle="combobox"
  data-sp-target="#combobox-grouped"
  aria-expanded="false"
>
  <span class="combobox-value" data-sp-placeholder="Select a language..."></span>
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
</button>
<div id="combobox-grouped" class="combobox" role="listbox">
  <div class="combobox-search">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
    <input class="combobox-input" type="text" placeholder="Search..." />
  </div>
  <div class="combobox-list">
    <div class="combobox-label">Frontend</div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="language" value="ts" />
      TypeScript
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="language" value="js" />
      JavaScript
    </div>
    <div class="combobox-separator"></div>
    <div class="combobox-label">Backend</div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="language" value="go" />
      Go
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="language" value="rust" />
      Rust
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="language" value="python" />
      Python
    </div>
  </div>
  <div class="combobox-empty">No language found.</div>
</div>

Disabled items

Use aria-disabled="true" and tabindex="-1" on a .combobox-item to prevent selection. Add disabled to the input as well.

Free
Pro
Enterprise
No plan found.
<button
  class="combobox-trigger w-56"
  type="button"
  data-sp-toggle="combobox"
  data-sp-target="#combobox-disabled"
  aria-expanded="false"
>
  <span class="combobox-value" data-sp-placeholder="Select a plan..."></span>
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
</button>
<div id="combobox-disabled" class="combobox" role="listbox">
  <div class="combobox-search">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
    <input class="combobox-input" type="text" placeholder="Search plans..." />
  </div>
  <div class="combobox-list">
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="plan" value="free" />
      Free
    </div>
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" class="sr-only" tabindex="-1" name="plan" value="pro" />
      Pro
    </div>
    <div class="combobox-item" role="option" tabindex="-1" aria-disabled="true">
      <input type="radio" class="sr-only" tabindex="-1" disabled="" name="plan" value="enterprise" />
      Enterprise
    </div>
  </div>
  <div class="combobox-empty">No plan found.</div>
</div>

How it works

Structure

A combobox consists of two elements linked by id:

  1. .combobox-trigger with data-sp-toggle="combobox" and data-sp-target="#id" — the trigger button containing a .combobox-value span
  2. .combobox with a matching id — the floating panel with search input and item list

Each item contains a visually hidden <input type="radio"> (single select) or <input type="checkbox"> (multi-select) with a name and value for form submission.

<button
  class="combobox-trigger"
  type="button"
  data-sp-toggle="combobox"
  data-sp-target="#my-combobox"
  aria-expanded="false"
>
  <span class="combobox-value" data-sp-placeholder="Select an option..."></span>
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
</button>
<div id="my-combobox" class="combobox" role="listbox">
  <div class="combobox-search">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
    <input class="combobox-input" type="text" placeholder="Search..." />
  </div>
  <div class="combobox-list">
    <div class="combobox-item" role="option" tabindex="0">
      <input type="radio" name="option" value="a" class="sr-only" tabindex="-1" />
      Option A
    </div>
  </div>
  <div class="combobox-empty">No results found.</div>
</div>

The trigger and the menu can live anywhere in the DOM as long as the id matches.

Opening and closing

Add data-sp-toggle="combobox" and data-sp-target to the trigger to toggle the linked menu on click. Clicking outside the combobox or pressing Escape closes the menu. For single select, clicking an item selects it and closes the menu. For multi-select, the menu stays open. The search input is cleared and items are reset when the menu closes.

For programmatic control, use the global sp.combobox module:

const trigger = document.querySelector("[data-sp-target='#my-combobox']");
const menu = document.getElementById("my-combobox");
const item = menu.querySelector(".combobox-item");
 
sp.combobox.open(trigger);
sp.combobox.close(menu);
sp.combobox.toggle(trigger);
sp.combobox.select(trigger, menu, item);
sp.combobox.filter(menu, "query");

Filtering

Typing in the search input automatically hides items whose text content does not match the query (case-insensitive). The .combobox-empty element becomes visible when no items match.

To filter programmatically, call sp.combobox.filter:

const menu = document.getElementById("my-combobox");
sp.combobox.filter(menu, "next");

Selection

A native change event is dispatched from the item's input and bubbles up through the DOM. Listen for it on the menu, a parent form, or any ancestor:

document.getElementById("my-combobox").addEventListener("change", (e) => {
  console.log(e.target.name, e.target.value, e.target.checked);
});

Animation

The combobox menu includes a default fade and zoom entrance animation. When opened, the JavaScript sets data-state="open" on the menu.

To disable the default animation and apply your own, add no-animation to the menu:

<div
  id="my-combobox"
  class="combobox no-animation data-[state=open]:animate-in data-[state=open]:slide-in-from-top-2"
>
  ...
</div>

Placement

Use data-sp-placement on the trigger to control where the menu appears relative to it (default is bottom-start). Use data-sp-offset to set the distance in pixels (default is 4).

<button
  class="combobox-trigger"
  data-sp-toggle="combobox"
  data-sp-target="#my-combobox"
  data-sp-placement="bottom-end"
  data-sp-offset="8"
>
  ...
</button>

Supported placement values follow the Floating UI convention: top, top-start, top-end, bottom, bottom-start, bottom-end, left, left-start, left-end, right, right-start, right-end.

Accessibility

Add aria-expanded="false" to the trigger button. It toggles to "true" when the menu opens. Items should have role="option" and tabindex="0" so they can receive keyboard focus.

<button
  class="combobox-trigger"
  data-sp-toggle="combobox"
  data-sp-target="#my-combobox"
  aria-expanded="false"
>
  ...
</button>
...
<div class="combobox-item" role="option" tabindex="0">
  <input type="radio" name="option" value="a" class="sr-only" tabindex="-1" />
  Option A
</div>

Keyboard navigation

KeyAction
Enter / SpaceOpens the menu when trigger is focused
EscapeCloses the menu and returns focus to the trigger
ArrowDownMove focus to the next visible item
ArrowUpMove focus to the previous visible item
HomeMove focus to the first visible item
EndMove focus to the last visible item
Enter / SpaceSelects the focused item

Class reference

ClassDescription
comboboxFloating panel containing the search input and list
combobox-triggerButton that opens and closes the menu
combobox-valueSpan inside the trigger holding selection children or placeholder
combobox-searchRow containing the search icon and input
combobox-inputBorderless text input for filtering options
combobox-listScrollable container for the list of options
combobox-itemIndividual selectable option
combobox-labelNon-interactive group heading
combobox-separatorHorizontal divider between groups
combobox-emptyMessage shown when no items match the search query
no-animationAdd to .combobox to disable the default open animation

Data attributes

AttributeElementDescription
data-sp-toggle="combobox"Trigger buttonMarks the trigger
data-sp-target="#id"Trigger buttonThe id of the linked .combobox menu
data-sp-placementTrigger buttonMenu placement relative to trigger (default: bottom-start)
data-sp-offsetTrigger buttonDistance from trigger in pixels (default: 4)
data-sp-submit-on-closeTrigger buttonSubmit the enclosing form (or form id) on close when selections changed
data-sp-submit-on-changeTrigger buttonSubmit the enclosing form (or form id) every time an item inside the menu is toggled
aria-expandedTrigger buttonSet "false" initially; JS toggles on open/close
data-sp-placeholder.combobox-valueEmpty-state text rendered via CSS when no children are present
data-sp-static.combobox-valueOpt out of JS-driven updates; placeholder becomes a permanent foreground label
data-state.comboboxSet to open when menu is visible
aria-disabled="true".combobox-itemDisables the item visually and functionally
aria-selected="true".combobox-itemMarks the item as currently selected