Interactive demo of native select field
/**
* 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
| Prop | Type | Default | Description |
|---|---|---|---|
type | 'native-select' | - | Field type (required) |
label | string | - | Label text |
placeholder | string | - | Placeholder option text |
helperText | string | - | Help text below field |
options | Option[] | - | Array of options |
value | string | - | Default selected value |
size | 'sm' | 'md' | 'lg' | 'md' | Select size variant |
columns | Partial<Record<Breakpoint, number>> | - | Grid columns by breakpoint |
disabled | boolean | function | ConditionGroup | false | Disable the select |
hidden | boolean | function | ConditionGroup | Responsive | false | Hide the field |
Native Select vs Radix Select
| Use Native Select | Use Radix Select |
|---|---|
| Mobile-first apps | Custom styling needed |
| Maximum compatibility | Rich interactions |
| Simple options | Complex option rendering |
| Form submissions | SPA 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"
}
Related Fields
Accessibility
- Native
<select>element for full accessibility - Works with all assistive technologies
- Keyboard navigation built-in
- Mobile-friendly with native picker UI