Skip to content

Select

Choose one option with the native select or, to search or choose several, with UICombobox.

UISelect is a native select with appearance: base-select: the browser brings the list, the keyboard and the form value, and UI paints it with its tokens. UICombobox covers what the native one cannot: searching, filtering on a server and choosing several options. The type of the model is inferred from options.

One option in a form

With name, a native form sends the value without extra code. Open the list with Enter, Space or a click and type a letter to jump to the option that starts with it: it is the keyboard of the browser.

Accessibility
Design
Documentation
Performance
Testing
Security

Up to two topics. The search ignores accents and case.

Choose a plan and up to two topics.

meeko
<script lang="ts">
  const plans = ['Personal', 'Team', 'Business', 'Education']
  const topics = ['Accessibility', 'Design', 'Documentation', 'Performance']
  const plan = signal<string | null>(null)
  const chosen = signal<string[]>([])
</script>

<template>
  <form method="post" action="/subscriptions">
    <label for="plan">Plan</label>
    <UISelect id="plan" model="plan" bind:options="plans"
      name="plan" placeholder="Choose a plan" required />

    <label for="topics">Topics</label>
    <UICombobox id="topics" model="chosen" bind:options="topics"
      name="topics" multiple searchable bind:max="2" />

    <UIButton type="submit">Send selection</UIButton>
  </form>
</template>
With required, a form without a value is not sent: the select receives the focus and aria-invalid, without the browser message. Write a visible message too with UIField, as in the example. With useForm, pass bind:field="form.field('plan')" and the field brings the id, the name, the value and the error state.

Browsers

Chrome, Edge and Safari 27 paint the list with the tokens of UI, with an animated entrance. Firefox and older browsers show their own list: they work the same, and there value, group and the rich content of option stay as their text. A hidden option at the start stands for the placeholder, or for a value that is no longer in options: the model keeps it and the form keeps sending it.

Objects, groups and custom options

Options can be objects. by decides how they are compared, labelBy which text is shown and searched, groupBy adds a heading per group and an option with disabled: true cannot be chosen. The option and value snippets receive the option with its type.

No owner assigned.

meeko
<script lang="ts">
  interface Member { id: number; name: string; team: string; role: string; disabled?: boolean }
  const members: Member[] = [
    { id: 1, name: 'Lucía Martín', team: 'Design', role: 'Art director' },
    { id: 4, name: 'Mateo Gil', team: 'Engineering', role: 'On vacation', disabled: true },
  ]
  const owner = signal<Member | null>(null)
</script>

<template>
  <UISelect model="owner" bind:options="members" by="id"
    labelBy="name" groupBy="team" label="Owner" clearable>
    <snippet name="option" params="member, state">
      <strong>{member.name}</strong>
      <small>{state.disabled ? 'Unavailable' : member.role}</small>
    </snippet>
  </UISelect>
</template>

UISelect API

UISelect props
Name Type / default Description
options readonly T[] Required. Primitives or objects.
model Signal<T | null> null Chosen value.
by / labelBy / disabledBy / groupBy key or function Comparison, label, disabled state and group of each option.
clearable / clearLabel boolean / string false / Clear selection Accessible button that clears the selection and returns focus to the select. The default label follows the language of the document (see Theming).
name / required / disabled string / boolean Form submission and validation.
field SelectField A useForm field: id, name, value, aria-invalid and aria-describedby.
id / label / describedBy / invalid string / boolean Identity and accessible state of the select.
placeholder string Text without a value.
placement / offset PopoverPlacement / number bottom-start Position of the list, where the browser paints it with the tokens.
class / listClass string Classes of the root and of the select; style the list with .my-class::picker(select).

It accepts the option, value and group snippets, and the change event. multiple, searchable, filter and max are a type error that points to UICombobox.

UICombobox

A list of its own on top of UIPopover, with more JavaScript: use it only if you need searchable, filter, multiple or max. The focus stays on the trigger while aria-activedescendant walks the list. The Topics field of the first example is a UICombobox. With multiple, each option toggles, the list stays open and the model is an array; max disables the rest at the limit.

meeko
<UICombobox id="topics" model="chosen" bind:options="topics"
  name="topics" multiple searchable bind:max="2" placeholder="Search for a topic" />

To search on a server, pass a filter function that returns a promise and uses its AbortSignal. Without results it shows empty: by default, No results.

UICombobox own props
Name Type / default Description
model Signal<T | null> or Signal<T[]> null Chosen value; an array with multiple.
multiple boolean false Allows choosing several options.
searchable / filter boolean / ComboboxFilter<T> Search in the list, synchronous or asynchronous.
max number With multiple, maximum number of chosen options.

The other props are those of UISelect. It also accepts the empty and loading snippets. The types UISelectProps, UIComboboxProps, SelectModel and ComboboxFilter are exported from @meekojs/ui/runtime.

Was this page helpful?

Add a comment

It is sent when you choose “Yes” or “No”. Only the page, your answer and the comment are stored: no cookies and no personal data.

0 of 2000 characters