Form
A form wrapper that coordinates validation across child input fields and handles submit/reset events.
Usage
Wrap your inputs in <Form>. Native <button type="submit"> and <button type="reset"> work as expected.
Anatomy
<script setup lang="ts">
import { Form, Input } from '@vuetify/v0'
</script>
<template>
<Form>
<Input.Root>
<Input.Control />
<Input.Error />
</Input.Root>
</Form>
</template>Architecture
Form creates a createForm instance and provides it via useForm(). Child validations auto-register on mount and unregister on unmount.
The @submit event is pass-through — it always fires, regardless of validity. Guard in your handler:
function onSubmit ({ valid }: { valid: boolean }) {
if (!valid) return
// handle submission
}Examples
Try submitting with taken@example.com to see server-side error injection.
Recipes
Slot Props
Use slot props for reactive form state in the template:
<template>
<Form v-slot="{ isValid, isValidating, submit, reset }">
<!-- Inputs -->
<button type="submit" :disabled="isValidating">
{{ isValidating ? 'Validating…' : 'Submit' }}
</button>
<p v-if="isValid === false">Please fix the errors above.</p>
</Form>
</template>Disabled / Readonly
The disabled and readonly props propagate to the form context. Child components can read them via useForm():
<template>
<Form disabled>
<!-- All fields read form.disabled via useForm() -->
</Form>
</template>Custom Namespace
Use namespace to isolate multiple forms on the same page. Keys must contain :. Child fields join via form-namespace — the same pairing as group-namespace on Toggle/Checkbox/Radio.
<template>
<Form namespace="v0:billing">
<Input.Root form-namespace="v0:billing" />
</Form>
<Form namespace="v0:shipping">
<Input.Root form-namespace="v0:shipping" />
</Form>
</template>form on Input is the HTML form="id" attribute. form-namespace is the DI key. Defaults match (v0:form), so a single unnamed Form needs no extra prop.
Programmatic Submit
Call submit() from slot props when you need to trigger validation without a submit button:
<template>
<Form v-slot="{ submit }">
<!-- Inputs -->
<button type="button" @click="submit">Save Draft</button>
</Form>
</template> Calling submit() or reset() via slot props invokes the form methods directly and does not emit @submit or @reset. Those events only fire from native form submission/reset.
Accessibility
Form renders a native <form> element, so all standard form semantics apply. No custom ARIA is needed — the browser handles submit on Enter, associates labels with inputs via id/for, and reports validation errors to assistive technology through child inputs.
Keyboard Interaction
| Key | Behavior |
|---|---|
Enter (in input) | Submits the form |
Escape | No default behavior — handle in your submit handler |
FAQ
Form defaults to novalidate, so the browser never runs native constraint validation. required on Input and NumberField still registers a presence rule with the Form, so empty required fields make submit() return false.
@submit is pass-through — it fires on every native submit regardless of validity. Guard inside the handler: read the valid flag from the payload and return early when it’s false.
Give each a namespace that contains : (e.g. namespace="v0:billing") and set the same value on each field’s form-namespace. Sibling forms are already isolated by Vue’s nearest provide; the custom key is for useForm('v0:billing') from outside the tree and for fields that would otherwise inject the default 'v0:form'.
submit() and reset() from slot props invoke the form methods directly. The @submit and @reset events only fire from native form submission or reset.