Comboboxes
Collect entries or chips in filterable fields.
Setup
Combobox styles are included in Manifest CSS or a standalone stylesheet, both referencing theme variables. The plugin is included in manifest.js with all core plugins, or it can be loaded on its own.
<!-- Manifest CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.css" />
<!-- Manifest JS -->
<script src="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.js"></script>
Triggers
Add x-combobox to the element that holds the selection and opens the list. Three elements work as triggers, and the rest of this guide uses an input.
Input
Type to filter or to enter free text.
<input x-combobox="countries" placeholder="Country">
Textarea
Textareas are effectively the same as inputs in style and function. The .multiple modifier enabled both inputs and textareas to support multiple entries, which wrap and grow the field's height on overflow.
<textarea x-combobox.multiple.chips="skills" placeholder="Add skills"></textarea>
Button
Buttons behave like a select menu trigger, with no typing input. A single field shows the choice as the button's text. A multiple field shows chips.
<button x-combobox="priority">Select priority</button>
<menu popover id="priority">
<li>Low</li>
<li>Medium</li>
<li>High</li>
</menu>
Chips
Add .chips to show selections as removable chips, and .multiple to allow more than one. Type a value and press Enter, or a comma, to commit it as a chip. Press Backspace on an empty field to remove the last one.
<input x-combobox.multiple.chips placeholder="Add emails">
Separators
Set your own commit keys with the separators option, where each character commits the current entry. So ", " commits on a comma or a space. Pasting text that already contains a separator splits it into several chips at once.
<input x-combobox.multiple.chips="{ separators: ', ' }" placeholder="Add tags">
Starting Values
Seed the field with a starting set using the value attribute, written with the same separators.
<input x-combobox.multiple.chips value="ana@acme.com, sam@acme.com">
Suggestions List
Point a field at a list of options to filter as the user types. Name the list by its id in the directive value, the same way dropdowns reference their menu. The list is a <datalist> or <select> kept in the page as the source.
A single field lets the user pick one option or type their own. Re-opening it shows the whole list again, so the choice can be swapped.
<input x-combobox="countries" placeholder="Country">
<datalist id="countries">
<option>Australia</option>
<option>Brazil</option>
<option>Canada</option>
</datalist>
Add .multiple.chips to choose several. Chosen options drop out of the list as they are picked.
<input x-combobox.multiple.chips="skills" placeholder="Skills">
<datalist id="skills">
<option>Design</option>
<option>Writing</option>
<option>Research</option>
</datalist>
Dropdown Menu
For richer rows, author the list as a <menu popover> instead of a datalist, same as dropdown menus. Give each item a data-value for the value to store and a data-label for the chip text. Everything inside the item is free to hold extra content.
<input x-combobox.multiple.chips="people" placeholder="Assign people">
<menu popover id="people">
<li data-value="ana" data-label="Ana Ruiz">Ana Ruiz <span class="trailing">ana@acme.com</span></li>
<li data-value="sam" data-label="Sam Cole">Sam Cole <span class="trailing">sam@acme.com</span></li>
</menu>
Options
Beyond modes, settings are passed as an object in the directive value. The bare id form is shorthand for the source key, so x-combobox="people" and x-combobox="{ source: 'people' }" are the same.
| Option | Purpose |
|---|---|
source |
ID of the <datalist>, <select>, or <menu> holding the options |
max |
Most selections allowed; omit for uncapped selections |
filter |
How typed text matches options. See below |
separators |
Characters that commit the current entry as a chip |
min |
Characters to type before remote results are fetched |
debounce |
Milliseconds to wait after typing before fetching. Default 200 |
Filtering
Set how typed text matches options with the filter option.
| Value | Match |
|---|---|
includes |
Text appears anywhere in the option (default mode) |
startswith |
Option begins with the text |
none |
No filtering; always show every option |
pattern |
Match each option's own data-pattern expression against the input |
<input x-combobox="{ source: 'countries', filter: 'startswith' }">
Modifiers
New Values
A field accepts anything the user types by default. Add .create to show an explicit "Add …" row for the current text when it does not match an option.
<input x-combobox.multiple.chips.create="skills" placeholder="Skills">
To restrict the field to the list and reject anything else, add .strict instead.
Limit
Cap the number of selections with the max option. Once the limit is reached the field stops accepting input until a chip is removed.
<input x-combobox.multiple.chips="{ source: 'skills', max: 3 }" placeholder="Pick up to 3">
Remote Suggestions
Add .async to fetch options as the user types instead of filtering a fixed list. On each keystroke the field sends a combobox-filter event carrying the typed value and a setOptions callback. Call setOptions with your results to fill the list.
<input x-combobox.async="{ min: 1 }" placeholder="Search users"
@combobox-filter="
fetch('/api/users?q=' + $event.detail.value)
.then(r => r.json())
.then(users => $event.detail.setOptions(users))
">
Each result is { value, label }, or a plain string when the two are the same. The field opens on focus, shows a brief loading state while it waits, and ignores out-of-date responses. Use min to set how many characters are needed before fetching, and debounce to set the pause after typing.
Form Participation
Add a name attribute to submit with a <form>. A single text field submits its value directly. A field with chips, or any multiple field, submits one entry per value under that name, which arrives as a list on the server.
<form>
<input x-combobox.multiple.chips="tags" name="tags" value="design, research">
<button type="submit">Save</button>
</form>
Localization
The few built-in UI strings are overridable through the universal _ui block, namespaced under combobox, in any local data file your project loads. Override only the keys you want. Anything omitted stays in English, and switching locale updates them live.
{
"data": {
"content": {
"en": "/data/content.en.yaml",
"fr": "/data/content.fr.yaml"
}
}
}
The add string places the typed text with a {value} token, so translations keep their own word order and punctuation.
| Key | Default | Used for |
|---|---|---|
empty |
No matches | Shown when nothing matches |
add |
Add “{value}” | The .create row label |
loading |
Searching… | Shown while remote results load |
prompt |
Type to search | Remote field before enough is typed |
Keyboard
| Key | Action |
|---|---|
↑ ↓ |
Move through the list |
Enter |
Select the highlighted option, or commit typed text |
Backspace |
Remove the last chip when the field is empty |
| Separator | Commit the current text as a chip |
Escape |
Close the list |
Styles
Theme
Comboboxes use the following theme variables.
| Variable | Purpose |
|---|---|
--color-field-surface |
Field background, and the highlighted option |
--color-field-surface-hover |
Field hover background |
--color-field-inverse |
Field text |
--color-popover-surface |
Suggestion menu and chip background |
--color-content-stark |
Chip text and focus ring |
--color-content-neutral |
Chip remove icon and the "Add …" row |
--color-content-subtle |
Caret and empty-state text |
--color-negative-surface / --color-negative-inverse |
Invalid chip |
--radius |
Field, chip, and menu corners |
--spacing-field-height |
Field height |
--spacing-field-padding |
Field padding |
--icon-chevron-down |
Button trigger caret |
--transition |
Interactive transitions |
Customization
The markup is semantic, so the parts are plain selectors.
| Selector | Part |
|---|---|
.combobox |
The field shell |
.combobox-chip |
A chip, with its label in a span and remove control in a button |
menu[role=listbox] |
The suggestion menu |
[role=option] |
An option |
[role=option][aria-selected=true] |
A chosen option |
[role=option][aria-current=true] |
The highlighted option |
/* Pill-shaped chips */
.combobox-chip {
border-radius: 100px;
}
Article does not exist
There is no documentation at this path.