Forms

Inputs, their wrapper, and the validation engine behind them. Every input works on its own, and picks up labelling automatically when wrapped in a form field.

Autocomplete

Free text with shortcuts. Unlike the combobox, whatever the user types is the value — the suggestions only save keystrokes, so an address nobody listed is still accepted.

Suggested pickup points

Any address is accepted

<UiAutocomplete v-model="pickup" :suggestions="suggestions" />
PropTypeDefaultDescription
suggestionsstring[]Shortcuts offered while typing.
minCharsnumber1Characters needed before the list opens.
loadingbooleanfalseShows a spinner while suggestions are fetched.
disabledbooleanfalseBlocks interaction.

Checkbox

States

<UiCheckbox v-model="accepted">Accept terms</UiCheckbox>
PropTypeDefaultDescription
indeterminatebooleanfalseMixed state, for "select all" checkboxes.
disabledbooleanfalseBlocks interaction.

Combobox

A select you can type into. The value always comes from the list, so text that matches nothing is discarded when the panel closes — the field can never be left holding something the server would reject.

Filter as you type

Type to filter

<UiCombobox v-model="route" :options="options" />
PropTypeDefaultDescription
optionsSelectOption[]Choices the value must come from.
placeholderstring'Search…'Shown while the box is empty.
noResultsTextstring'No results'Shown when the filter matches nothing.
disabledbooleanfalseBlocks interaction.

Date picker

Values are plain YYYY-MM-DD strings built from local date parts, never from toISOString() — which would shift the date a day backwards for anyone east of UTC.

Single date

<UiDatePicker v-model="departure" />

Date range

Pick a start, then an end

<UiDatePicker v-model="stay" mode="range" />
PropTypeDefaultDescription
mode'single' | 'range''single'One date, or a start and an end.
minstringundefinedEarliest selectable date, as YYYY-MM-DD.
maxstringundefinedLatest selectable date, as YYYY-MM-DD.
placeholderstring'Select a date'Shown while nothing is chosen.
disabledbooleanfalseBlocks interaction.

File upload

Drag and drop or browse. Rejected files say why they were rejected instead of vanishing, and image previews release their object URLs when a file is removed — otherwise the browser holds the whole file in memory for as long as the page is open.

Images up to 2 MB

Drop files here, or click to browse

Accepted: image/*

Up to 2 MB each

PNG or JPG, up to 2 MB each

<UiFileUpload v-model="attachments" accept="image/*" multiple :max-size="2 * 1024 * 1024" />
PropTypeDefaultDescription
acceptstringundefinedSame syntax as the native input: "image/*,.pdf".
multiplebooleanfalseAppend files instead of replacing.
maxSizenumberundefinedLargest accepted file, in bytes.
disabledbooleanfalseBlocks interaction.

Form field

Label, hint and error

Work address

<UiFormField label="Email" hint="Work address" required>
PropTypeDefaultDescription
labelstringundefinedField label, linked to the input.
hintstringundefinedHelper text. Hidden while an error is showing.
errorstringundefinedError message. Marks the input invalid.
requiredbooleanfalseShows a required marker next to the label.

Input

Sizes, icon and clearable

<UiInput v-model="q" icon="lucide:search" clearable />
PropTypeDefaultDescription
type'text' | 'email' | 'password' | 'search' | 'tel' | 'url''text'Native input type.
placeholderstringundefinedPlaceholder text.
size'sm' | 'md' | 'lg''md'Control height.
disabledbooleanfalseBlocks interaction.
readonlybooleanfalseValue cannot be edited.
iconstringundefinedLeading icon id.
clearablebooleanfalseShows a clear button while the field has a value.

Money input

The value stays a number; only the display is formatted. While the field has focus the raw number is shown, so editing never fights the thousands separators.

Formatted amount

$
<UiMoneyInput v-model="price" prefix="$" />
PropTypeDefaultDescription
prefixstringundefinedCurrency symbol shown inside the field.
localestring'en-US'Locale used for grouping separators.
precisionnumber0Decimal places.
disabledbooleanfalseBlocks interaction.

Number input

An empty field means null, not zero — so "not filled in yet" stays distinguishable from a deliberate zero.

With bounds

<UiNumberInput v-model="quantity" :min="0" :max="10" />
PropTypeDefaultDescription
minnumberundefinedLower bound. Values are clamped to it.
maxnumberundefinedUpper bound. Values are clamped to it.
stepnumber1Increment used by the stepper buttons.
disabledbooleanfalseBlocks interaction.

Radio

Group

<UiRadio v-model="frequency" value="daily" name="frequency">Daily</UiRadio>
PropTypeDefaultDescription
valuestringValue this radio represents.
namestringGroup name shared by the radios.
disabledbooleanfalseBlocks interaction.

Select

A listbox rather than a native select, so the menu can be styled and skinned. Arrow keys, Home, End, Enter and Escape all behave the way a native control does, and disabled options are skipped instead of merely greyed out.

Single choice

<UiSelect v-model="city" :options="options" />
PropTypeDefaultDescription
optionsSelectOption[]Choices, each with a value and a label.
placeholderstring'Select an option'Shown while nothing is selected.
size'sm' | 'md' | 'lg''md'Control height.
disabledbooleanfalseBlocks interaction.

Slider

Built on a native range input, so keyboard support, screen reader announcements and touch gestures all behave correctly without extra code.

Value slider

40
<UiSlider v-model="volume" :min="0" :max="100" />
PropTypeDefaultDescription
minnumber0Lower bound.
maxnumber100Upper bound.
stepnumber1Increment.
showValuebooleantrueShows the current value beside the track.
disabledbooleanfalseBlocks interaction.

Switch

States

<UiSwitch v-model="alerts" label="Email alerts" />
PropTypeDefaultDescription
labelstringRequired accessible name.
size'sm' | 'md''md'Track size.
disabledbooleanfalseBlocks interaction.

Textarea

With character counter

0 / 200

<UiTextarea v-model="notes" :maxlength="200" />
PropTypeDefaultDescription
rowsnumber4Visible line count.
placeholderstringundefinedPlaceholder text.
disabledbooleanfalseBlocks interaction.
maxlengthnumberundefinedCharacter limit. Shows a counter when set.

useForm

Validation rules are plain functions, so they are easy to read, compose and test. Rules stop at the first failure per field — showing "required" and "too short" together helps nobody.

One pair is easy to mix up. required() accepts false and 0 — they are real values for a number field, and rejecting them would be wrong. For a checkbox that must be ticked, reach for requiredTrue() instead; it is the only rule that treats an unticked box as a failure.

Declaring a form

const form = useForm(
  { name: '', mail: '' },
  { name: [required(), minLength(3)], mail: [required(), email()] },
)

await form.submit(async (values) => save(values))
PropTypeDefaultDescription
valuesTinitialReactive form values.
errorsRecord<keyof T, string | null>all nullFirst error per field.
touchedRecord<keyof T, boolean>all falseWhether a field has been validated yet.
isValidComputedRef<boolean>falseTrue once validate() has run and found no errors.
validateField(field) => booleanValidates one field and marks it touched.
validate() => booleanValidates every field.
reset() => voidRestores initial values, clears errors and touched.
submit(handler) => Promise<void>Validates, then calls the handler only when valid.