react-formbridge
Browse documentation
Field buildersv2.0.0

field.select()

Single-choice picker for finite option lists.

  • Use options(list) for local options
  • Use optionsFrom(fetcher, config) for remote datasets
  • Add searchable() when a richer lookup experience is needed
  • Use defaultSelected(...) / selected(...) when the form should open on a meaningful choice instead of an empty placeholder
Select.web.tsxtsx
1const schema = {
2 workspace: field.select('Workspace')
3 .options([
4 { label: 'Operations workspace', value: 'ops' },
5 { label: 'Revenue cockpit', value: 'revenue' },
6 { label: 'Support command center', value: 'support' },
7 ])
8 .defaultSelected('revenue')
9 .required(),
10}

optionsFrom config

  • key
  • cacheTtl (default 60000)
  • debounce (default 300)
  • minChars (default 0)
  • dependsOn (default [])
  • initialOptions (default [])
  • fetchOnMount (default true)
  • keepPreviousOptions (default true)
  • preserveOnError (default true)

Render a picker

Select fields expose their resolved options through fieldController(name). Use them in a native <select>, a design-system combobox, a modal, or a bottom sheet.

InlineExample-1.tsxtsx
1const schema = {
2 city: field
3 .select('City')
4 .optionsFrom(fetchCities, {
5 key: 'city-search',
6 debounce: 250,
7 minChars: 2,
8 })
9 .searchable(),
10}
11
12const form = useFormBridge(schema)
13const city = form.fieldController('city')
14
15<select
16 value={String(city.value ?? '')}
17 disabled={city.disabled}
18 onChange={(event) => city.onChange(event.target.value)}
19 onBlur={city.onBlur}
20>
21 {city.options?.map((option) => (
22 <option key={String(option.value)} value={option.value}>
23 {option.label}
24 </option>
25 ))}
26</select>

For remote search, use useAsyncOptions() to own query/loading/error state and pass its options to your picker UI.

Defaults, inheritance & field methods

  • defaultValue is ''
  • type is select
  • Selected values are stored as the option value, so both string and number are supported
  • Shared methods: see Base field builder
  • String-specific helpers are not available on select fields
  • Picker UIs consume form.fieldController(name) and remain entirely application-owned

Select-specific methods:

MethodTypeDescription
options(list)string[] | { label, value }[]Loads local options from plain strings or explicit label/value objects.
optionsFrom(fetcher, config)fetcher + async configLoads remote options with debounce, cache, dependency tracking, and loading state support.
searchable(value = true)value?: booleanEnables search-oriented picker behavior.
defaultSelected(valueOrOption)value | option objectPre-selects an option by raw value or option object.
selected(valueOrOption)value | option objectAlias for defaultSelected() with more explicit wording.
oneOf(values, message?)Array<value | option>Additional allow-list check on top of options(). Useful when the option list is broader than what the current form should actually accept (e.g. gating promo tiers by user role).
notOneOf(values, message?)Array<value | option>Deny-list version of oneOf() - rejects a subset of values even if they exist in options().
disallowPlaceholder(message?)message?: stringShorthand for required() with a select-oriented default message ("Please select an option."). Use it when the placeholder row is visible but must not be submitted.

Recipes

Patterns that showcase select-specific strengths.

Preselected workspace

Workspace.tsxtsx
1const WORKSPACE_OPTIONS = [
2 { label: 'Operations workspace', value: 'ops' },
3 { label: 'Revenue cockpit', value: 'revenue' },
4 { label: 'Support command center', value: 'support' },
5]
6
7const schema = {
8 workspace: field.select('Workspace')
9 .options(WORKSPACE_OPTIONS)
10 .defaultSelected('revenue')
11 .required(),
12}

Numeric option values

SeatPack.tsxtsx
1const schema = {
2 seatPack: field.select('Seat pack')
3 .options([
4 { label: 'Starter · 5 seats', value: 5 },
5 { label: 'Growth · 12 seats', value: 12 },
6 { label: 'Scale · 25 seats', value: 25 },
7 ])
8 .defaultSelected(12),
9}

Async city search with debounce

CitySearch.tsxtsx
1const schema = {
2 city: field.select('City')
3 .optionsFrom(fetchCities, {
4 key: 'city-search',
5 debounce: 250,
6 minChars: 2,
7 })
8 .searchable(),
9}

Dependent cascading selects

RegionCascade.tsxtsx
1const schema = {
2 country: field.select('Country').options(COUNTRIES).required(),
3 region: field.select('Region')
4 .optionsFrom(fetchRegions, {
5 key: 'regions',
6 dependsOn: ['country'],
7 }),
8}

Fully custom trigger

CustomTrigger.tsxtsx
1const schema = {
2 routingMode: field.select('Routing mode')
3 .options(ROUTING_MODES),
4}
5
6const form = useFormBridge(schema)
7const controller = form.fieldController('routingMode')
8
9<form.Form onSubmit={save}>
10 <MyTrigger
11 label={controller.value ?? 'Select mode'}
12 onClick={controller.openPicker}
13 />
14 <MyModal
15 open={controller.isOpen}
16 options={controller.options}
17 onSelect={controller.setValue}
18 onClose={controller.closePicker}
19 />
20</form.Form>