Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Reference for Nuxt UI v4 (125+ components built on Reka UI + Tailwind CSS v4) including forms, overlays, and theming.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/forms.md
1# Forms23## Form Components45| Component | Purpose | Key Props | Version |6| ---------------- | ------------------- | --------------------------------------------- | ------- |7| `UInput` | Text input | `type`, `placeholder`, `icon`, `loading` | |8| `UTextarea` | Multi-line text | `rows`, `autoresize`, `maxrows` | |9| `USelect` | Native select | `options`, `placeholder` | |10| `USelectMenu` | Custom select | `options`, `searchable`, `multiple` | |11| `UInputMenu` | Autocomplete | `options`, `searchable` | |12| `UCheckbox` | Single checkbox | `label`, `description` | |13| `UCheckboxGroup` | Multiple checkboxes | `items`, `orientation` | |14| `URadioGroup` | Radio options | `items`, `orientation` | |15| `USwitch` | Toggle switch | `label`, `description`, `on-icon`, `off-icon` | |16| `USlider` | Range slider | `min`, `max`, `step` | |17| `UPinInput` | Code/OTP input | `length`, `type`, `mask` | |18| `UInputNumber` | Number input | `min`, `max`, `step`, `format-options` | |19| `UInputDate` | Date picker | `mode`, `range`, `locale` | v4.2+ |20| `UInputTime` | Time picker | `hour-cycle`, `minute-step` | v4.2+ |21| `UInputTags` | Tag input | `max`, `placeholder` | |22| `UColorPicker` | Color selection | `format`, `swatches` | |23| `UFileUpload` | File upload | `accept`, `multiple`, `max-files` | |2425## Basic Input Examples2627```vue28<script setup>29const email = ref('')30const bio = ref('')31const country = ref('')32</script>3334<template>35<!-- Text input -->36<UInput v-model="email" type="email" placeholder="Email" icon="i-heroicons-envelope" />3738<!-- With validation state -->39<UInput v-model="email" :color="emailError ? 'error' : undefined" />4041<!-- Textarea -->42<UTextarea v-model="bio" placeholder="Bio" :rows="3" autoresize />4344<!-- Select -->45<USelect v-model="country" :options="['USA', 'Canada', 'Mexico']" placeholder="Country" />46</template>47```4849## SelectMenu (Custom Dropdown)5051```vue52<script setup>53const selected = ref()54const options = [55{ label: 'John', value: 'john', avatar: { src: '/john.png' } },56{ label: 'Jane', value: 'jane', avatar: { src: '/jane.png' } }57]58</script>5960<template>61<USelectMenu62v-model="selected"63:options="options"64searchable65clear66placeholder="Select user"67>68<template #option="{ option }">69<UAvatar v-bind="option.avatar" size="xs" />70<span>{{ option.label }}</span>71</template>72</USelectMenu>73</template>74```7576### SelectMenu/InputMenu Props (v4.4+)7778```vue79<USelectMenu80v-model="selected"81:options="options"82clear <!-- Add clear button (v4.4+) -->83:viewport-ref="ref" <!-- For infinite scroll (v4.4+) -->84/>85```8687## Checkbox & Radio8889```vue90<script setup>91const agreed = ref(false)92const plan = ref('free')93const features = ref([])94</script>9596<template>97<!-- Single checkbox -->98<UCheckbox v-model="agreed" label="I agree to terms" description="Required" />99100<!-- Radio group -->101<URadioGroup102v-model="plan"103:items="[104{ label: 'Free', value: 'free', description: '$0/mo' },105{ label: 'Pro', value: 'pro', description: '$10/mo' }106]"107/>108109<!-- Checkbox group -->110<UCheckboxGroup111v-model="features"112:items="[113{ label: 'Dark mode', value: 'dark' },114{ label: 'Notifications', value: 'notifications' }115]"116/>117</template>118```119120## Form Validation121122Uses Standard Schema (Zod, Valibot, Yup, Joi, etc.)123124### With Zod125126```vue127<script setup lang="ts">128import { z } from 'zod'129130const schema = z.object({131email: z.string().email('Invalid email'),132password: z.string().min(8, 'Min 8 characters')133})134135type Schema = z.output<typeof schema>136137const state = reactive<Partial<Schema>>({138email: '',139password: ''140})141142const form = ref()143144async function onSubmit() {145await form.value.validate()146// Submit logic147}148</script>149150<template>151<UForm ref="form" :schema="schema" :state="state" @submit="onSubmit">152<UFormField name="email" label="Email">153<UInput v-model="state.email" type="email" />154</UFormField>155156<UFormField name="password" label="Password">157<UInput v-model="state.password" type="password" />158</UFormField>159160<UButton type="submit">Submit</UButton>161</UForm>162</template>163```164165### With Valibot166167```vue168<script setup lang="ts">169import * as v from 'valibot'170171const schema = v.object({172email: v.pipe(v.string(), v.email('Invalid email')),173password: v.pipe(v.string(), v.minLength(8, 'Min 8 characters'))174})175176type Schema = v.InferOutput<typeof schema>177178const state = reactive<Partial<Schema>>({179email: '',180password: ''181})182</script>183184<template>185<UForm :schema="schema" :state="state" @submit="onSubmit">186<!-- Same as above -->187</UForm>188</template>189```190191## UFormField Props192193```vue194<UFormField195name="email" <!-- Field name (matches state key) -->196label="Email" <!-- Label text -->197description="Your email" <!-- Help text -->198hint="Optional" <!-- Right-aligned hint -->199required <!-- Shows asterisk -->200:help="error?.message" <!-- Error message -->201>202<UInput v-model="state.email" />203</UFormField>204```205206## UFieldGroup (Group Fields)207208```vue209<UFieldGroup label="Name">210<UInput v-model="firstName" placeholder="First" />211<UInput v-model="lastName" placeholder="Last" />212</UFieldGroup>213```214215## Input States216217```vue218<!-- Disabled -->219<UInput disabled placeholder="Disabled" />220221<!-- Loading -->222<UInput :loading="true" placeholder="Loading..." />223224<!-- With icon -->225<UInput icon="i-heroicons-magnifying-glass" placeholder="Search" />226227<!-- Trailing icon -->228<UInput trailing-icon="i-heroicons-x-mark" placeholder="Clearable" />229```230231## File Upload232233```vue234<script setup>235const { files, open, reset } = useFileUpload()236</script>237238<template>239<UFileUpload v-model="files" accept="image/*" multiple :max-files="5">240<UButton @click="open">Upload</UButton>241</UFileUpload>242</template>243```244245**Note (v4.4):** FileUpload now emits `null` when clearing files (previously emitted empty array).246247## Date & Time Pickers (v4.2+)248249### Date Picker250251```vue252<script setup>253const date = ref(new Date())254const range = ref({ start: new Date(), end: new Date() })255</script>256257<template>258<!-- Single date -->259<UInputDate v-model="date" />260261<!-- Date range -->262<UInputDate v-model="range" mode="range" />263264<!-- With locale -->265<UInputDate v-model="date" locale="es" />266</template>267```268269### Time Picker270271```vue272<script setup>273import { Time } from '@internationalized/date'274275const time = ref(new Time(12, 0))276</script>277278<template>279<!-- Basic time picker -->280<UInputTime v-model="time" />281282<!-- 24-hour format -->283<UInputTime v-model="time" hour-cycle="24" />284285<!-- Custom step (minutes) -->286<UInputTime v-model="time" :minute-step="15" />287</template>288```289290## Common Patterns291292### Login Form293294```vue295<UForm :schema="loginSchema" :state="state" @submit="login">296<UFormField name="email" label="Email" required>297<UInput v-model="state.email" type="email" icon="i-heroicons-envelope" />298</UFormField>299<UFormField name="password" label="Password" required>300<UInput v-model="state.password" type="password" icon="i-heroicons-lock-closed" />301</UFormField>302<UButton type="submit" block :loading="loading">Sign in</UButton>303</UForm>304```305306### Settings Form307308```vue309<UForm :state="settings" @submit="save">310<UFormField name="notifications" label="Notifications">311<USwitch v-model="settings.notifications" label="Enable notifications" />312</UFormField>313<UFormField name="theme" label="Theme">314<URadioGroup v-model="settings.theme" :items="themeOptions" />315</UFormField>316<UButton type="submit">Save</UButton>317</UForm>318```319320## Best Practices321322| Do | Don't |323| --------------------------------- | -------------------------- |324| Use UForm + UFormField | Build custom form wrappers |325| Use Standard Schema (Zod/Valibot) | Write custom validation |326| Use v-model on form inputs | Manually sync form state |327| Use `required` prop for asterisks | Add asterisks manually |328| Use `description` for help text | Use separate help elements |329