Select
This is a real <select> with the browser’s own dropdown — which means it works with no JavaScript, opens correctly on a phone, and is keyboard-navigable without anything from this library.
select() is the bare control; selectField() wraps it in a label, help text and error message, the same way textField() does.
Basic select
Options can be plain strings, in which case the value and the label are the same.
selectField({
label: 'Theme',
name: 'theme',
options: ['Light', 'Dark', 'System'],
})Values and labels
Pass objects when the value that gets submitted differs from the text a person reads. value marks the selected option.
selectField({
label: 'Output',
name: 'output',
value: 'dist',
options: [
{ value: 'dist', label: 'dist/ — the default' },
{ value: 'build', label: 'build/' },
{ value: 'public', label: 'public/', disabled: true },
],
})Placeholder
A placeholder renders as a disabled first option, selected when value is absent — so the field starts empty without being a valid choice.
selectField({
label: 'Deploy target',
name: 'target',
placeholder: 'Choose a host…',
options: ['Netlify', 'Vercel', 'Cloudflare Pages', 'GitHub Pages'],
})Groups
An entry with its own options array becomes an <optgroup>.
selectField({
label: 'Page extension',
name: 'ext',
value: '.ht.js',
options: [
{ label: 'JavaScript', options: ['.ht.js', '.html.js'] },
{ label: 'TypeScript', options: ['.ht.ts', '.html.ts'] },
{ label: 'JSX', options: ['.ht.jsx', '.ht.tsx'] },
],
})Sizes
stack({ gap: 'md' },
selectField({ label: 'Small', name: 'sm', size: 'sm', options: ['One', 'Two'] }),
selectField({ label: 'Medium', name: 'md', size: 'md', options: ['One', 'Two'] }),
selectField({ label: 'Large', name: 'lg', size: 'lg', options: ['One', 'Two'] }),
)Help, error and disabled
stack({ gap: 'lg' },
selectField({
label: 'Locale',
name: 'locale',
options: ['en', 'es', 'fr'],
help: 'Used for the html lang attribute.',
}),
selectField({
label: 'Framework',
name: 'framework',
placeholder: 'Pick one…',
options: ['sitelo'],
error: 'Choose a framework to continue.',
}),
selectField({
label: 'Plan',
name: 'plan',
options: ['Free'],
disabled: true,
}),
)From data
Options are just an array, so they usually come from whatever data() already loaded for the page.
return (() => {
const posts = [
{ slug: 'hello-world', title: 'Hello world' },
{ slug: 'static-first', title: 'Static first' },
{ slug: 'no-runtime', title: 'No runtime' },
]
return selectField({
label: 'Featured post',
name: 'featured',
value: 'static-first',
options: posts.map((post) => ({ value: post.slug, label: post.title })),
})
})()Props
| Prop | Type | Default | Description |
|---|---|---|---|
options | SelectOption[] | [] | Strings, { value, label, disabled } objects, or { label, options } for a group. |
value | string | number | — | Which option is selected. |
placeholder | string | — | Disabled first option, selected when there is no value. |
size | 'sm' | 'md' | 'lg' | 'md' | Control height and text size. |
name | string | — | Form field name; the id is derived from it. |
invalid | boolean | false | Sets aria-invalid. selectField sets this for you from error. |
disabled | boolean | false | Disables the control. |
selectField() additionally takes label, help, error, required and fieldClass — see textField(). Children are appended after the generated options, so you can hand-write any you need.