Input
Headless text input with integrated validation and automatic form registration.
Usage
The Input supports text, email, password, and other native input types. Validation rules run on blur by default, with lazy and eager modifiers available.
Basic Input with Validation
Email input with validation rules, inline error display, and helper text.
Anatomy
<script setup lang="ts">
import { Input } from '@vuetify/v0'
</script>
<template>
<Input.Root>
<Input.Control />
<Input.Description />
<Input.Error />
</Input.Root>
</template>Architecture
Root creates a validation context, provides it to children, and manages focus/validation lifecycle. Control is the native <input> (or any element via as). Description and Error auto-wire their IDs into Control’s ARIA attributes.
Examples
Contact Form
Multi-field form with createForm integration, lazy validation, and server-side error injection. Each Input.Root creates its own createValidation and auto-registers with the parent form.
File breakdown:
| File | Role |
|---|---|
useContact.ts | Composable — form instance, field refs, submit with server-side validation |
ContactForm.vue | Reusable component — three Input fields with different rules and types |
contact-form.vue | Demo — wires composable to form, shows submitted data |
Key patterns:
validateOn="blur lazy"defers validation until the user blurs the field for the first time, then validates on every subsequent blur:errorand:error-messagesinject server-side errors after submit — the email field shows “already registered” whentaken@example.comis usedInput.Control as="textarea"renders the message field as a native textarea while keeping all validation and ARIA wiring
Try submitting with taken@example.com to see server-side error injection.
Live Search
Debounced search with validateOn="input" for real-time validation. The composable watches the Input’s value ref directly — no event wiring needed.
File breakdown:
| File | Role |
|---|---|
useSearch.ts | Composable — debounced search with mock results, watches the query ref |
SearchInput.vue | Reusable component — Input with search icon, loading spinner, result count |
search.vue | Demo — renders SearchInput with a result list |
Key patterns:
validateOn="input"validates on every keystroke (minimum 2 characters)The composable watches the query ref with
useTimerfrom@vuetify/v0, demonstrating thatvalueis a standard writable Refdata-[focused]:border-primaryanddata-[state=invalid]:border-errorstyle the input purely through data attributes — no slot props needed for visual states
Recipes
validateOn Modes
Control when validation runs with the validateOn prop and optional lazy/eager modifiers:
<template>
<!-- Validate on blur (default) -->
<Input.Root validate-on="blur" />
<!-- Validate on every keystroke -->
<Input.Root validate-on="input" />
<!-- Only validate on form submit -->
<Input.Root validate-on="submit" />
<!-- Lazy: skip validation until first blur, then validate on blur -->
<Input.Root validate-on="blur lazy" />
<!-- Eager: after first error, validate on every keystroke -->
<Input.Root validate-on="blur eager" />
</template>Manual Error State
Override validation with the error and error-messages props for server-side errors:
<template>
<Input.Root
:error="!!serverError"
:error-messages="serverError"
:rules="[(v) => !!v || 'Required']"
>
<Input.Control />
<Input.Error v-slot="{ errors }">
<span v-for="e in errors" :key="e">{{ e }}</span>
</Input.Error>
</Input.Root>
</template>Data Attributes
Style interactive states without slot props:
<template>
<Input.Control class="data-[focused]:border-primary data-[state=invalid]:border-error" />
</template>| Attribute | Values | Components |
|---|---|---|
data-state | pristine, valid, invalid | Root, Control |
data-dirty | true | Root |
data-focused | true | Root, Control |
data-disabled | true | Root, Control |
data-readonly | true | Root, Control |
Accessibility
Input.Control renders as a native <input> and manages all ARIA attributes automatically.
ARIA Attributes
| Attribute | Value | Notes |
|---|---|---|
aria-invalid | true | When validation fails or error prop is set |
aria-label | Label text | From Root’s label prop |
aria-describedby | Description ID | Only present when Input.Description is mounted |
aria-errormessage | Error ID | Only present when Input.Error is mounted and errors exist |
aria-required | true | From Root’s required prop |
required | true | Native attribute, from Root’s required prop |
disabled | true | Native attribute, from Root’s disabled prop |
readonly | true | Native attribute, from Root’s readonly prop |
Keyboard Navigation
Standard native <input> keyboard behavior. No custom key handlers — the browser handles focus, selection, and editing.
Input.Root
Props
error
boolean | undefinedManual error state override — forces invalid regardless of validation
Default: false
Events
update:model-value
[value: string]update:isFocused
[value: boolean]Slots
default
InputRootSlotPropsInput.Control
Props
Slots
default
InputControlSlotPropsInput.Description
Props
Slots
default
InputDescriptionSlotPropsInput.Error
Props
Slots
default
InputErrorSlotProps