S
Selection Field

Native Select

Native HTML select dropdown for simple selection needs with maximum browser compatibility.

stable
select native dropdown html

Interactive demo of native select field

Where are you located?

/**
 * Native Select Field Demo - Interactive examples of native select fields
 */

import { Form, FormBuilder } from '@saastro/forms';

import { FormProvider } from '@/components/FormProvider';
import { TooltipProvider } from '@/components/ui/tooltip';

const config = FormBuilder.create('native-select-demo')
  .layout('manual')
  .columns(12)
  .addField('country', (f) =>
    f
      .type('native-select')
      .label('Country')
      .placeholder('Select a country')
      .helperText('Where are you located?')
      .options([
        { label: 'United States', value: 'us' },
        { label: 'Canada', value: 'ca' },
        { label: 'Mexico', value: 'mx' },
        { label: 'United Kingdom', value: 'uk' },
        { label: 'Germany', value: 'de' },
        { label: 'France', value: 'fr' },
      ])
      .required('Please select a country')
      .columns({ default: 12, md: 6 }),
  )
  .addField('language', (f) =>
    f
      .type('native-select')
      .label('Language')
      .placeholder('Select language')
      .options([
        { label: 'English', value: 'en' },
        { label: 'Spanish', value: 'es' },
        { label: 'French', value: 'fr' },
        { label: 'German', value: 'de' },
      ])
      .value('en')
      .required()
      .columns({ default: 12, md: 6 }),
  )
  .addField('size', (f) =>
    f
      .type('native-select')
      .label('T-Shirt Size')
      .placeholder('Select size')
      .options([
        { label: 'Extra Small (XS)', value: 'xs' },
        { label: 'Small (S)', value: 's' },
        { label: 'Medium (M)', value: 'm' },
        { label: 'Large (L)', value: 'l' },
        { label: 'Extra Large (XL)', value: 'xl' },
      ])
      .required()
      .columns({ default: 12 }),
  )
  .addStep('main', ['country', 'language', 'size'])
  .build();

export default function NativeSelectDemo() {
  const handleSubmit = (data: Record<string, unknown>) => {
    console.log('Form submitted:', data);
    alert('Form submitted! Check console for data.');
  };

  return (
    <TooltipProvider>
      <FormProvider>
        <Form config={config} onSubmit={handleSubmit} className="space-y-4" />
      </FormProvider>
    </TooltipProvider>
  );
}

Overview

The native select field uses the browser’s built-in <select> element. It’s ideal when you need maximum compatibility, mobile-friendly dropdowns, or when the Radix Select is overkill.

Usage

Basic Native Select

import { FormBuilder } from '@saastro/forms';

const config = FormBuilder.create('form')
  .addField('country', (f) =>
    f
      .type('native-select')
      .label('Country')
      .placeholder('Select a country')
      .options([
        { label: 'United States', value: 'us' },
        { label: 'Canada', value: 'ca' },
        { label: 'Mexico', value: 'mx' },
      ])
      .required('Please select a country'),
  )
  .addStep('main', ['country'])
  .build();

With Default Value

.addField('language', (f) =>
  f.type('native-select')
    .label('Language')
    .options([
      { label: 'English', value: 'en' },
      { label: 'Spanish', value: 'es' },
      { label: 'French', value: 'fr' },
    ])
    .value('en')
    .required()
)

With Option Groups

.addField('timezone', (f) =>
  f.type('native-select')
    .label('Timezone')
    .options([
      { label: 'America/New_York', value: 'america-new-york' },
      { label: 'America/Los_Angeles', value: 'america-la' },
      { label: 'Europe/London', value: 'europe-london' },
      { label: 'Europe/Paris', value: 'europe-paris' },
      { label: 'Asia/Tokyo', value: 'asia-tokyo' },
    ])
    .required()
)

JSON Configuration

{
  "type": "native-select",
  "label": "Country",
  "placeholder": "Select a country",
  "options": [
    { "label": "United States", "value": "us" },
    { "label": "Canada", "value": "ca" },
    { "label": "Mexico", "value": "mx" }
  ],
  "schema": { "required": true }
}

Props

PropTypeDefaultDescription
type'native-select'-Field type (required)
labelstring-Label text
placeholderstring-Placeholder option text
helperTextstring-Help text below field
optionsOption[]-Array of options
valuestring-Default selected value
size'sm' | 'md' | 'lg''md'Select size variant
columnsPartial<Record<Breakpoint, number>>-Grid columns by breakpoint
disabledboolean | function | ConditionGroupfalseDisable the select
hiddenboolean | function | ConditionGroup | ResponsivefalseHide the field

Native Select vs Radix Select

Use Native SelectUse Radix Select
Mobile-first appsCustom styling needed
Maximum compatibilityRich interactions
Simple optionsComplex option rendering
Form submissionsSPA applications

Validation

Required Selection

.addField('size', (f) =>
  f.type('native-select')
    .label('Size')
    .options([
      { label: 'Small', value: 's' },
      { label: 'Medium', value: 'm' },
      { label: 'Large', value: 'l' },
    ])
    .required('Please select a size')
)

Styling

Custom Classes

.addField('country', (f) =>
  f.type('native-select')
    .label('Country')
    .options([...])
    .required()
    .classNames({
      wrapper: 'bg-muted/30 p-3 rounded-lg',
      input: 'border-primary bg-background',
      label: 'text-sm font-medium',
      error: 'text-destructive text-sm',
    })
)

Responsive Layout

.addField('language', (f) =>
  f.type('native-select')
    .label('Language')
    .options([...])
    .columns({ default: 12, md: 6, lg: 4 })
    .size('lg')
)

JSON Styling

{
  "type": "native-select",
  "label": "Country",
  "wrapper_className": "bg-muted/30 p-3 rounded-lg",
  "input_className": "border-primary",
  "label_className": "text-sm font-medium",
  "columns": { "default": 12, "md": 6 },
  "size": "md"
}
  • Select - Radix UI select with custom styling
  • Combobox - Searchable select
  • Radio - Visible options

Accessibility

  • Native <select> element for full accessibility
  • Works with all assistive technologies
  • Keyboard navigation built-in
  • Mobile-friendly with native picker UI