Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Guidance for building UIs with Nuxt UI, the official Tailwind-based component library for Nuxt.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/guidelines/forms.md
1# Forms23## Basic pattern45Nuxt UI forms use `UForm` + `UFormField` + Standard Schema validation (Zod, Valibot, Yup, or Joi).67```vue8<script setup lang="ts">9import * as z from 'zod'10import type { FormSubmitEvent } from '@nuxt/ui'1112const schema = z.object({13email: z.email('Invalid email'),14password: z.string().min(8, 'Min 8 characters')15})1617type Schema = z.output<typeof schema>18const state = reactive<Partial<Schema>>({ email: '', password: '' })1920function onSubmit(event: FormSubmitEvent<Schema>) {21// UForm validates before emitting @submit — access validated data via event.data22}23</script>2425<template>26<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">27<UFormField name="email" label="Email" required>28<UInput v-model="state.email" type="email" placeholder="[email protected]" />29</UFormField>3031<UFormField name="password" label="Password" required>32<UInput v-model="state.password" type="password" placeholder="Min 8 characters" />33</UFormField>3435<UButton type="submit" label="Sign in" />36</UForm>37</template>38```3940## Key rules4142- Always use `UFormField` around inputs — it connects validation errors via the `name` prop43- The `name` prop on `UFormField` must match the schema field name exactly44- Use `reactive<Partial<Schema>>({})` for state — `Partial` allows empty initial values45- `@submit` only fires when validation passes46- For nested objects, use dot notation: `name="address.city"`4748## UFormField props4950| Prop | Purpose |51|---|---|52| `name` | Links to schema field for validation errors |53| `label` | Visible label text |54| `description` | Help text below the input |55| `hint` | Right-aligned hint text (e.g., "Optional") |56| `required` | Shows required indicator |57| `size` | Inherits to child input |5859## Field layout patterns6061### Vertical stack (default)6263```vue64<UForm :schema="schema" :state="state" class="space-y-4">65<UFormField name="name" label="Name">66<UInput v-model="state.name" />67</UFormField>68<UFormField name="email" label="Email">69<UInput v-model="state.email" />70</UFormField>71</UForm>72```7374### Inline fields with UFieldGroup7576```vue77<UFieldGroup>78<UFormField name="firstName" label="First name">79<UInput v-model="state.firstName" />80</UFormField>81<UFormField name="lastName" label="Last name">82<UInput v-model="state.lastName" />83</UFormField>84</UFieldGroup>85```8687### Grid layout8889```vue90<UForm :schema="schema" :state="state" class="grid grid-cols-2 gap-4">91<UFormField name="firstName" label="First name">92<UInput v-model="state.firstName" />93</UFormField>94<UFormField name="lastName" label="Last name">95<UInput v-model="state.lastName" />96</UFormField>97<UFormField name="email" label="Email" class="col-span-2">98<UInput v-model="state.email" type="email" />99</UFormField>100</UForm>101```102103## Common field patterns104105### Select106107```vue108<UFormField name="role" label="Role">109<USelect v-model="state.role" :items="['Admin', 'Editor', 'Viewer']" placeholder="Choose role" />110</UFormField>111```112113### Checkbox114115```vue116<UFormField name="terms">117<UCheckbox v-model="state.terms" label="I agree to the terms and conditions" />118</UFormField>119```120121### Radio group122123```vue124<UFormField name="plan" label="Plan">125<URadioGroup126v-model="state.plan"127:items="[128{ label: 'Free', value: 'free', description: 'For personal projects' },129{ label: 'Pro', value: 'pro', description: 'For teams' }130]"131/>132</UFormField>133```134135### Switch136137```vue138<UFormField name="notifications" label="Email notifications">139<USwitch v-model="state.notifications" />140</UFormField>141```142143### Textarea144145```vue146<UFormField name="bio" label="Bio" description="Brief description for your profile.">147<UTextarea v-model="state.bio" :rows="3" autoresize :maxrows="6" />148</UFormField>149```150151### File upload152153```vue154<UFormField name="avatar" label="Avatar">155<UFileUpload v-model="state.avatar" accept="image/*" />156</UFormField>157158<!-- Or as a drop area -->159<UFormField name="documents" label="Documents">160<UFileUpload v-model="state.documents" multiple variant="area" />161</UFormField>162```163164### Date165166```vue167<UFormField name="date" label="Date">168<UInputDate v-model="state.date" />169</UFormField>170171<!-- Date range -->172<UFormField name="dateRange" label="Date range">173<UInputDate v-model="state.dateRange" range />174</UFormField>175```176177## Programmatic validation178179```vue180<script setup lang="ts">181const form = useTemplateRef('form')182183async function validateAndSubmit() {184const result = await form.value?.validate()185if (result) {186// valid — submit187}188}189190async function validateEmail() {191await form.value?.validate({ name: 'email' })192}193194function setServerError() {195form.value?.setErrors([196{ name: 'email', message: 'Email already taken' }197])198}199200function resetErrors() {201form.value?.clearErrors()202}203</script>204205<template>206<UForm ref="form" :schema="schema" :state="state" @submit="onSubmit">207<!-- fields -->208</UForm>209</template>210```211212## Form in a modal213214Use `#footer="{ close }"` scoped slot for cancel/submit actions. Wrap the modal body in `UForm` with a `type="submit"` button in the footer so validation runs on submit.215216```vue217<UModal v-model:open="isOpen" title="Edit profile" description="Update your information." :ui="{ footer: 'justify-end' }">218<template #body>219<UForm id="profile-form" :schema="schema" :state="state" class="space-y-4" @submit="onSave">220<UFormField name="name" label="Name">221<UInput v-model="state.name" />222</UFormField>223<UFormField name="email" label="Email">224<UInput v-model="state.email" type="email" />225</UFormField>226</UForm>227</template>228<template #footer="{ close }">229<UButton label="Cancel" color="neutral" variant="outline" @click="close" />230<UButton type="submit" form="profile-form" label="Save" />231</template>232</UModal>233```234