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" />| Prop | Type | Default | Description |
|---|---|---|---|
| suggestions | string[] | — | Shortcuts offered while typing. |
| minChars | number | 1 | Characters needed before the list opens. |
| loading | boolean | false | Shows a spinner while suggestions are fetched. |
| disabled | boolean | false | Blocks interaction. |
Checkbox
States
<UiCheckbox v-model="accepted">Accept terms</UiCheckbox>| Prop | Type | Default | Description |
|---|---|---|---|
| indeterminate | boolean | false | Mixed state, for "select all" checkboxes. |
| disabled | boolean | false | Blocks 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" />| Prop | Type | Default | Description |
|---|---|---|---|
| options | SelectOption[] | — | Choices the value must come from. |
| placeholder | string | 'Search…' | Shown while the box is empty. |
| noResultsText | string | 'No results' | Shown when the filter matches nothing. |
| disabled | boolean | false | Blocks 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" />| Prop | Type | Default | Description |
|---|---|---|---|
| mode | 'single' | 'range' | 'single' | One date, or a start and an end. |
| min | string | undefined | Earliest selectable date, as YYYY-MM-DD. |
| max | string | undefined | Latest selectable date, as YYYY-MM-DD. |
| placeholder | string | 'Select a date' | Shown while nothing is chosen. |
| disabled | boolean | false | Blocks 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" />| Prop | Type | Default | Description |
|---|---|---|---|
| accept | string | undefined | Same syntax as the native input: "image/*,.pdf". |
| multiple | boolean | false | Append files instead of replacing. |
| maxSize | number | undefined | Largest accepted file, in bytes. |
| disabled | boolean | false | Blocks interaction. |
Form field
Label, hint and error
Work address
Enter a valid email address
<UiFormField label="Email" hint="Work address" required>| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | undefined | Field label, linked to the input. |
| hint | string | undefined | Helper text. Hidden while an error is showing. |
| error | string | undefined | Error message. Marks the input invalid. |
| required | boolean | false | Shows a required marker next to the label. |
Input
Sizes, icon and clearable
<UiInput v-model="q" icon="lucide:search" clearable />| Prop | Type | Default | Description |
|---|---|---|---|
| type | 'text' | 'email' | 'password' | 'search' | 'tel' | 'url' | 'text' | Native input type. |
| placeholder | string | undefined | Placeholder text. |
| size | 'sm' | 'md' | 'lg' | 'md' | Control height. |
| disabled | boolean | false | Blocks interaction. |
| readonly | boolean | false | Value cannot be edited. |
| icon | string | undefined | Leading icon id. |
| clearable | boolean | false | Shows 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="$" />| Prop | Type | Default | Description |
|---|---|---|---|
| prefix | string | undefined | Currency symbol shown inside the field. |
| locale | string | 'en-US' | Locale used for grouping separators. |
| precision | number | 0 | Decimal places. |
| disabled | boolean | false | Blocks 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" />| Prop | Type | Default | Description |
|---|---|---|---|
| min | number | undefined | Lower bound. Values are clamped to it. |
| max | number | undefined | Upper bound. Values are clamped to it. |
| step | number | 1 | Increment used by the stepper buttons. |
| disabled | boolean | false | Blocks interaction. |
Radio
Group
<UiRadio v-model="frequency" value="daily" name="frequency">Daily</UiRadio>| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | — | Value this radio represents. |
| name | string | — | Group name shared by the radios. |
| disabled | boolean | false | Blocks 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" />| Prop | Type | Default | Description |
|---|---|---|---|
| options | SelectOption[] | — | Choices, each with a value and a label. |
| placeholder | string | 'Select an option' | Shown while nothing is selected. |
| size | 'sm' | 'md' | 'lg' | 'md' | Control height. |
| disabled | boolean | false | Blocks 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
<UiSlider v-model="volume" :min="0" :max="100" />| Prop | Type | Default | Description |
|---|---|---|---|
| min | number | 0 | Lower bound. |
| max | number | 100 | Upper bound. |
| step | number | 1 | Increment. |
| showValue | boolean | true | Shows the current value beside the track. |
| disabled | boolean | false | Blocks interaction. |
Switch
States
<UiSwitch v-model="alerts" label="Email alerts" />| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | — | Required accessible name. |
| size | 'sm' | 'md' | 'md' | Track size. |
| disabled | boolean | false | Blocks interaction. |
Textarea
With character counter
0 / 200
<UiTextarea v-model="notes" :maxlength="200" />| Prop | Type | Default | Description |
|---|---|---|---|
| rows | number | 4 | Visible line count. |
| placeholder | string | undefined | Placeholder text. |
| disabled | boolean | false | Blocks interaction. |
| maxlength | number | undefined | Character 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))| Prop | Type | Default | Description |
|---|---|---|---|
| values | T | initial | Reactive form values. |
| errors | Record<keyof T, string | null> | all null | First error per field. |
| touched | Record<keyof T, boolean> | all false | Whether a field has been validated yet. |
| isValid | ComputedRef<boolean> | false | True once validate() has run and found no errors. |
| validateField | (field) => boolean | — | Validates one field and marks it touched. |
| validate | () => boolean | — | Validates every field. |
| reset | () => void | — | Restores initial values, clears errors and touched. |
| submit | (handler) => Promise<void> | — | Validates, then calls the handler only when valid. |