Combobox New Alpha Light DOM & Tailwind

Open in Storybook

Lets users choose one or several options from a list by typing to filter, then picking with the keyboard or pointer.

OverviewUsageStandalone or inside a FieldCompositionExamplesAccessibilityAPI referencePartsDependenciesDesign guidelines
This component is considered alpha. We're still finalising the API, so it may include a breaking change at any time — use it at your own risk.
Loading...

Usage

Combobox lets people choose one option (or several) from a list by typing to filter, then picking with the keyboard or pointer. Reach for it when a Select would be unwieldy because there are many options to scan.

Combobox renders its built-in label, hint and error on its own. Wrapped in a Field, the Field takes over with a Field Label, Field Description and Field Error, and the built-in versions step aside automatically.

Import the combobox; add the option parts when you author options declaratively (importing the combobox also registers them):

Copy code
import "@nordhealth/components/lib/Combobox"
import "@nordhealth/components/lib/ComboboxOption"
import "@nordhealth/components/lib/ComboboxGroup"

Options can be authored declaratively as child elements or set as a data array — see Composition.

For a free-text input with suggestions (where the typed text is the value), see Autocomplete.

Standalone or inside a Field

Use the control on its own with its built-in label, hint and error, or wrap it in a Field — the Field then provides the labelling. Both render the same control:

InlineWith Field

Composition

There are two ways to supply options. Author them declaratively as children of <nord-combobox>, like a native <select> — no JavaScript required — or set the .options property for dynamic data.

Copy code
Combobox
├── ComboboxOption
├── ComboboxOption
└── ComboboxGroup        (optional)
    ├── ComboboxOption
    └── ComboboxOption
Copy code
<nord-combobox>
  <nord-combobox-option value="next">Next.js</nord-combobox-option>
  <nord-combobox-group label="Meta-frameworks">
    <nord-combobox-option value="astro">Astro</nord-combobox-option>
  </nord-combobox-group>
</nord-combobox>

Combobox Option

<nord-combobox-option> declares a single option:

  • value — the value submitted with the form and tracked in the combobox's value.
  • disabled — marks the option unselectable; it stays visible but is skipped during keyboard navigation.
  • label — text used for filtering and the accessible name. Defaults to the element's text content.

The option's child markup becomes the rendered row, so you can author custom items — an avatar, secondary text, an icon — without an html string or a render function. Set label when the content isn't plain text, so filtering and the accessible name have something to match on.

Combobox Group

<nord-combobox-group label="…"> clusters the <nord-combobox-option> children inside it under a heading; each contained option inherits the group's label.

Copy code
Combobox
└── ComboboxGroup        (label)
    ├── ComboboxOption
    └── ComboboxOption

Data-driven options

For dynamic, async or very large (virtualized) lists, set the .options property — an array of { value, label } objects — instead of declaring children. It's bound like any framework prop: :options in Vue, options={} in React, .options=${} in Lit. When .options is set it takes precedence over any declarative children.

Examples

Multiple

Add multiple to select several options. Selected options appear as removable chips. Pair it with clearable when users need to clear all selected chips at once. Set the initial selection with the .value property as an array.

Creatable

Add creatable to offer the typed query as a new option. When the query doesn't match an existing option, a highlighted "Create" row (hinted with a ⏎ Kbd) appears; selecting it adds the value as an option and selects it automatically — no JavaScript required. A create event still fires for notification; call event.preventDefault() in the handler to opt out and add the option yourself (for example, to map the typed text to a different value).

Add external-filtering to take over filtering yourself — listen for input, fetch matches, and assign them to .options. Set loading to show a spinner while the request is in flight. A preselected value keeps its label even before its option has loaded.

Async search (multiple)

The same async pattern works with multiple: selections become chips while typing fetches fresh matches. Use clearable to offer one-step clearing for the selected chips.

Virtualized

Add virtualized for very large lists. The combobox renders only the options in view (windowing), so a list of thousands stays fast. Rows are assumed to be a uniform height; virtualization is skipped for grouped lists.

Clearable

A clear (✕) button is shown by default whenever there is a value. Set the clearable property to false (e.g. .clearable = false, or :clearable="false" / clearable={false} in a framework) to hide it. The button only controls that affordance — a single-select value can always be cleared by deleting the input text.

Grouped options

Cluster related options under a heading: wrap declarative options in a <nord-combobox-group label="…">, or give each .options entry a group.

Auto highlight

Add auto-highlight to highlight the first matching option as the user types, so it can be committed by pressing Enter.

Invalid

Mark the surrounding Field as invalid to show the error state.

Custom items

For declarative options, author custom content as markup inside <nord-combobox-option> (see Composition). For data-driven .options, provide a .renderOption function (or a per-option html string) to render richer rows.

Disabled items

Mark individual options with disabled to keep them visible but unselectable. They are skipped during keyboard navigation.

Readonly

Set readonly to show the current value while preventing edits. The control stays focusable so its value can still be read, but the list cannot be opened.

Open on focus

By default the list opens when the combobox receives focus. Set open-on-focus to false to require a keystroke or click before the list appears.

Input group

Combobox composes inside an Input Group so you can pair it with buttons or other controls as a single unit.

Preselected value

Set the .value property to preselect an option. The combobox shows its label on load, even before the user opens the list.

Inside a dialog

Combobox positions its list correctly when used inside a Modal or other dialog, so the options are never clipped by the dialog bounds.

RTL

Combobox follows the document or container direction. Toggle the direction to see the layout mirror.

Accessibility

  • Give Combobox an accessible name with its built-in label, or — inside a Field — a Field Label; use Field Error for validation messages.
  • The list, active option and selection state are wired up with ARIA, and the highlighted option is tracked with aria-activedescendant so screen reader users hear the current choice while filtering.
  • The full keyboard pattern is supported: type to filter, move through options with the arrow keys, commit with Enter, and dismiss the list with Escape.
  • When clearable is set, the clear button has an accessible label so its purpose is announced.

API reference

Combobox

Combobox lets users choose one option (or several) from a list by typing to filter, then picking with the keyboard or pointer. Use it when a Select would be unwieldy because there are many options to scan. Combobox is a control only — it has no built-in label, hint or error. Compose it inside a Field to give it a label, hint and error message, the same way you would a native input. The committed value is an option's value (or an array of them with multiple); the input is a filter/search field whose text is discarded on commit. For a free-text input with suggestions where the typed text is the value, use Autocomplete instead.

<nord-combobox></nord-combobox>

Props

PropertyAttribute Description TypeDefault
valuevalueThe value of the combobox. A single string when multiple is false, or an array of strings when multiple is true.string''
multiplemultipleAllows selecting more than one option. Selected options are shown as removable chips and the listbox stays open after each selection.booleanfalse
clearableclearableShows a clear (✕) button while there is a value. Pressing it resets the value to '' (or [] when multiple), then fires clear and change. Defaults to true. This prop only toggles the button. A single-select value can always be cleared by deleting the input text, regardless of this prop (Base UI parity).booleantrue
creatablecreatableOffers a "Create" affordance when the query does not exactly match an existing option. Choosing it (click or Enter) adds the typed value as an option and selects it (in multiple mode it is appended as a chip), and fires a cancelable create event. Call event.preventDefault() to opt out and handle creation yourself (e.g. to map the value to a different value).booleanfalse
sizesizeThe size of the component.'s' | 'm' | 'l''m'
autocompleteautocompleteSpecifies the data type of the field, so that the browser may attempt to fill out the field automatically on behalf of the user.AutocompleteAttribute'off'
requiredrequiredDetermines whether the control is required or not. A required control is announced as such to assistive technology and, inside a <nord-field>, drives the required indicator on the <nord-field-label>. When using this property you need to also set “novalidate” attribute on a form element to prevent browser from displaying its own validation errors.booleanfalse
disableddisabledMakes the component disabled. This prevents users from being able to interact with the component, and conveys its inactive state to assistive technologies.booleanfalse
namenameThe name of the form component.string | undefined
formformGets the form, if any, associated with the form element. The setter accepts a string, which is the id of the form.HTMLFormElement | null
openopenControls whether the component is open or not.booleanfalse
alignalignSet the alignment in relation to the toggle (or anchor) depending on the position. start will align it to the left of the toggle (or anchor). end will align it to the right of the toggle (or anchor). Setting the position to inline-start or inline-end will switch start and end to the top and bottom respectively.'start' | 'end''start'
positionpositionSet the position in relation to the toggle (or anchor). Options follow logical properties. block-start and block-end referring to top and bottom respectively, inline-start and inline-end referring to left and right respectively. You can also set it to auto for automatic positioning depending on which side has the most space available.'block-end' | 'block-start' | 'inline-start' | 'inline-end' | 'auto''block-end'
sideOffsetside-offsetDistance in pixels from the toggle (or anchor) along the main axis — the gap between the floating element and the side it opens against. Fed into Floating UI's offset middleware as mainAxis. Defaults to 8.number8
alignOffsetalign-offsetOffset in pixels along the alignment axis — slides the floating element toward the start or end of the toggle (or anchor). Fed into Floating UI's offset middleware as alignmentAxis. Defaults to 0.number0
placeholderplaceholderPlaceholder text to display within the input.string | undefined
externalFilteringexternal-filteringUse external filtering mode. When set to true, the component will not perform internal text-based filtering and expects the consumer to update the options in response to the input event.booleanfalse
loadingloadingShows a spinner in the control and listbox while asynchronous results are loading. Pair with external-filtering for async search.booleanfalse
autoHighlightauto-highlightHighlights the first option after filtering so pressing Enter selects it.booleanfalse
invalidinvalidMarks the control as invalid, applying error styling and setting aria-invalid on the input.booleanfalse
readonlyreadonlyMarks the control read-only: the value stays visible, focusable and submitted with the form, but the listbox can't be opened and the value can't be changed (no typing, navigation, selection or clearing). Unlike disabled, a read-only control still participates in form submission.booleanfalse
openOnFocusopen-on-focusWhether focusing or clicking into the input opens the listbox. Defaults to true. Set to false to only open on typing or a trigger (chevron) click.booleantrue
noResultsTextno-results-textMessage shown when no options match the query. Defaults to "No items found.".string | undefined
virtualizedvirtualizedVirtualizes (windows) the option list so only the rows in view are rendered, keeping very large lists (thousands of options) fast. Rows are assumed to be a uniform height, which is measured from the first option. Virtualization is skipped for grouped lists, whose labels and separators break that assumption — grouped lists are expected to be short and curated.booleanfalse

Field properties

PropertyAttribute Description TypeDefault
labellabelLabel for the control. Ignored when the control is wrapped in a <nord-field>, which provides the label via <nord-field-label>.string''
hinthintOptional hint text shown with the control. Ignored inside a <nord-field>, which provides it via <nord-field-description>.string | undefined
hintBelowhint-belowRenders the hint below the control and any error instead of below the label.booleanfalse
hideLabelhide-labelVisually hide the label, but still expose it to assistive technologies.booleanfalse
errorerrorOptional error shown with the control. Ignored inside a <nord-field>, which provides it via <nord-field-error>.string | undefined
hideRequiredhide-requiredVisually hide the required indicator, but still expose the required state to assistive technologies.booleanfalse

Slots

Slot name Description
startOptional slot used to place an icon or prefix at the start of the control.
endOptional slot used to place an icon or suffix at the end of the control.

Methods

Method nameParameters Description
show() => voidN/AShow the listbox programmatically.
hide() => voidN/AHide the listbox programmatically.
focus(options?: FocusOptions) => voidoptions: FocusOptionsFocus the control's input.
EventDetail TypeDescription
changeNordEventDispatched when the committed value changes via user interaction.
inputNordEventDispatched as the user types into the search input.
openNordEventDispatched when the listbox is opened.
closeNordEventDispatched when the listbox is closed.
clearComboboxClearEventDispatched when the value is cleared via the clear button or clear().
createComboboxCreateEventDispatched when the user chooses the "Create" affordance in a creatable combobox.

CSS Properties

CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.

PropertyDescriptionDefault
--n-combobox-list-inline-sizeControls the inline size, or width, of the dropdown list. Defaults to the width of the combobox control.
--n-combobox-block-sizeControls the block size, or height, of the control. The medium default is var(--n-space-xl); the s and l sizes scale from it.

State attributes

These data-* attributes are set by the component to reflect its current state. Use them as styling hooks — they are read-only and should not be set manually.

AttributeDescription
data-invalidPresent while the control is invalid (mirrors the invalid property). Style hook for the whole control.
data-focusedPresent while the control has focus. Style hook for focus-within treatments.
Light DOM & Tailwind. This component renders in the light DOM, so you can style it directly with your own CSS or Tailwind utility classes — there is no shadow boundary, and its default styles are low specificity, so your utilities win.

Parts

This component is made up of the following parts.

Combobox Group

Groups declarative <nord-combobox-option> children under a heading inside a Combobox: ``html <nord-combobox> <nord-combobox-group label="Frameworks"> <nord-combobox-option value="next">Next.js</nord-combobox-option> </nord-combobox-group> </nord-combobox> ` Contained options inherit this label as their group. The element renders nothing itself and is hidden with display: none`.

<nord-combobox-group></nord-combobox-group>

Props

PropertyAttribute Description TypeDefault
labellabelThe group heading shown above its options.string''

Slots

Slot name Description
Default slotThe group's <nord-combobox-option> children.

Combobox Option

A declarative option for a Combobox. Place these as children of <nord-combobox> instead of (or as an alternative to) setting the .options property: ``html <nord-combobox> <nord-combobox-option value="bug">Bug</nord-combobox-option> <nord-combobox-option value="feature">Feature</nord-combobox-option> </nord-combobox> ` The option's child markup becomes the rendered row, so you can author custom items (e.g. an avatar + text) without an html string or a render function. Set the label attribute when the visible markup isn't plain text, so filtering and the accessible name have something to match on. The element renders nothing itself — it is a data source the combobox reads via context and is hidden with display: none`.

<nord-combobox-option></nord-combobox-option>

Props

PropertyAttribute Description TypeDefault
valuevalueThe value submitted with the form and tracked in the combobox's value.string''
disableddisabledWhether the option is selectable.booleanfalse
labellabelText used for filtering and the accessible name. Defaults to the text content.string | undefined

Slots

Slot name Description
Default slotThe option's label / custom content.
Design guidelinesFor designers

Usage

Combobox is a control only — it has no built-in label, hint or error. Pair it with a Field to give it a label, helper text and an error message, exactly like a native input. Connect the Field Label to the combobox with the for attribute so it gets an accessible name and click-to-focus.

Import the combobox and the Field parts you need — each import registers its custom element:

Copy code
import '@nordhealth/components/lib/Combobox'
import '@nordhealth/components/lib/Field'
import '@nordhealth/components/lib/FieldLabel'
import '@nordhealth/components/lib/FieldDescription'
import '@nordhealth/components/lib/FieldError'

Then compose the Field around the combobox:

Copy code
<nord-field>
  <nord-field-label for="framework">Framework</nord-field-label>
  <nord-combobox id="framework" placeholder="Select a framework"></nord-combobox>
  <nord-field-description>Choose the framework for your project.</nord-field-description>
  <!-- show a Field Error and mark the Field invalid instead, when validation fails -->
</nord-field>

Provide the choices with the options property — pairing it with a Field only handles labelling and layout, it doesn't change how you populate the list:

Copy code
document.querySelector('nord-combobox').options = [
  { value: 'next', label: 'Next.js' },
  { value: 'astro', label: 'Astro' },
]

Combobox renders in the light DOM, so you can style it directly with your own CSS or Tailwind utilities, and set its width on the combobox (or on its Field).

Use the size property (s, m or l, defaulting to m) to scale the control's height, padding and font size to match surrounding form controls.

Select vs free-text suggestions

The committed value is an option's value. The input is a search/filter field whose text is discarded on commit; closing the listbox reverts the input to the selected option's label. Nothing is committed unless the user picks an option, so the value is always one of the options you provide (or an array of them with multiple).

If you instead need the typed text itself to be the committed value — free-text input with the options as completion aids — use Autocomplete. Autocomplete also offers inline ghost-text completion, where arrow-key navigation previews the highlighted option's remaining label.

Picking an option vs creating one

Both multiple and creatable keep the value made of option values — they never produce arbitrary free text (use Autocomplete for that):

  • multiple lets users pick several options, each shown as a removable chip; the listbox stays open after every selection.
  • creatable offers a Create row (hinted with a ⏎ Kbd) when the query matches no option. Choosing it fires a create event so you add the option — the combobox never mutates options. With multiple, append the created option to value yourself.

The inner input always hard-codes autocomplete="off" so browser autofill never fights the suggestion list.

Do

  • Use when users need to pick from a long list of options and benefit from typing to filter, such as countries, timezones or frameworks.
  • Pair it with a Field and a Field Label connected with for, so it has an accessible name, helper text and an error message.
  • Use multiple when users may legitimately pick more than one option, showing each choice as a removable chip.
  • Use external-filtering and update options from the input event when the list comes from a server or is too large to filter on the client.
  • Group related options with the group property to help users scan long lists.

Don’t

  • Don’t use when there are only a handful of options and filtering adds no value, see Select instead.
  • Don’t add a Field Label without a for pointing at the combobox’s id, or the control will have no accessible name.
  • Don’t use for free-form text entry, see Input instead.
  • Don’t use to trigger actions or commands, see Command Menu instead.

Was this page helpful?

Yes No

We use this feedback to improve our documentation.