Skip to main content
On this pageOverview

Select

Overview

A wrapper around the native <select> element with ARIA label/description linking and data-attribute hooks. Select is a view-only component — for a custom dropdown with keyboard navigation and custom rendering, use Listbox or Combobox instead.

See it in an app

Check out how Select is wired up in a real Foldkit app.

Examples

Basic

Pass an onChange handler that receives the selected option’s value as a string. You provide the <option> elements inside the <select> in your toView callback.

Where you currently reside.
// Pseudocode — Select is view-only. The selected value lives in your own
// Model as a string. Replace model.country and UpdatedCountry with your
// own field and Message.
import { Ui } from 'foldkit'

import { Class, Value, div, label, option, select, span } from './html'

Ui.Select.view({
  id: 'country',
  value: model.country, // your Model field
  onChange: value => UpdatedCountry({ value }), // your Message
  toView: attributes =>
    div(
      [Class('flex flex-col gap-1.5')],
      [
        label([...attributes.label, Class('text-sm font-medium')], ['Country']),
        select(
          [...attributes.select, Class('w-full rounded-lg border px-3 py-2')],
          [
            option([Value('us')], ['United States']),
            option([Value('ca')], ['Canada']),
            option([Value('gb')], ['United Kingdom']),
          ],
        ),
        span(
          [...attributes.description, Class('text-sm text-gray-500')],
          ['Where you currently reside.'],
        ),
      ],
    ),
})

Disabled

Set isDisabled: true to disable the select.

This select is disabled.
// Pseudocode — Select is view-only. Disabled selects display a fixed value
// and ignore onChange events.
import { Ui } from 'foldkit'

import { Class, Value, label, option, select, span } from './html'

Ui.Select.view({
  id: 'country-disabled',
  isDisabled: true,
  value: 'us',
  toView: attributes =>
    div(
      [Class('flex flex-col gap-1.5')],
      [
        label([...attributes.label, Class('text-sm font-medium')], ['Country']),
        select(
          [
            ...attributes.select,
            Class(
              'w-full rounded-lg border px-3 py-2 data-[disabled]:opacity-50',
            ),
          ],
          [
            option([Value('us')], ['United States']),
            option([Value('ca')], ['Canada']),
          ],
        ),
        span(
          [...attributes.description, Class('text-sm text-gray-500')],
          ['This select is disabled.'],
        ),
      ],
    ),
})

Styling

Select is headless — your toView callback controls all markup and styling. The native <select> dropdown appearance varies by browser and OS. Use appearance-none in CSS and add a custom chevron icon for a consistent look.

AttributeCondition
data-disabledPresent when isDisabled is true.
data-invalidPresent when isInvalid is true.

Keyboard Interaction

Select uses the native <select> element, so keyboard interaction is handled by the browser.

KeyDescription
SpaceOpens the native dropdown.
EnterOpens the native dropdown.
Arrow Up/DownNavigates between options.

Accessibility

Select provides the same ARIA wiring as Input. The label group links via for, and the description group is referenced by aria-describedby. You can access the description ID directly with Select.descriptionId(id).

API Reference

ViewConfig

Configuration object passed to Select.view().

NameTypeDefaultDescription
idstring-Unique ID for the select element. Used to link the label and description via ARIA attributes.
toView(attributes: SelectAttributes) => Html-Callback that receives attribute groups for the select, label, and description elements.
onChange(value: string) => Message-Function that maps the selected value to a Message when the selection changes.
valuestring-The currently selected value.
isDisabledbooleanfalseWhether the select is disabled. Sets both the native disabled attribute and aria-disabled.
isInvalidbooleanfalseWhether the select is in an invalid state. Sets aria-invalid and adds a data-invalid attribute for styling.
isAutofocusbooleanfalseWhether the select receives focus when the page loads.
namestring-The form field name for native form submission.

SelectAttributes

Attribute groups provided to the toView callback.

NameTypeDefaultDescription
selectReadonlyArray<Attribute<Message>>-Spread onto the <select> element. Includes id, value, ARIA attributes, and event handlers.
labelReadonlyArray<Attribute<Message>>-Spread onto the <label> element. Includes a for attribute linking to the select id.
descriptionReadonlyArray<Attribute<Message>>-Spread onto a description element. Includes an id that the select references via aria-describedby.

Stay in the update loop.

New releases, patterns, and the occasional deep dive.


Built with Foldkit.

© 2026 Devin Jameson