Skip to main content
Vuetify0 is now in alpha!
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

Input

Headless text input with integrated validation and automatic form registration.


Renders elementIntermediateApr 5, 2026

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.

We'll never share your email.

Anatomy

vue
<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.

Input Architecture

Use controls to zoom and pan. Click outside or press Escape to close.

Input Architecture

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:

FileRole
useContact.tsComposable — form instance, field refs, submit with server-side validation
ContactForm.vueReusable component — three Input fields with different rules and types
contact-form.vueDemo — 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

  • :error and :error-messages inject server-side errors after submit — the email field shows “already registered” when taken@example.com is used

  • Input.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.

Debounced search with validateOn="input" for real-time validation. The composable watches the Input’s value ref directly — no event wiring needed.

File breakdown:

FileRole
useSearch.tsComposable — debounced search with mock results, watches the query ref
SearchInput.vueReusable component — Input with search icon, loading spinner, result count
search.vueDemo — renders SearchInput with a result list

Key patterns:

  • validateOn="input" validates on every keystroke (minimum 2 characters)

  • The composable watches the query ref with useTimer from @vuetify/v0, demonstrating that value is a standard writable Ref

  • data-[focused]:border-primary and data-[state=invalid]:border-error style 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:

vue
<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:

vue
<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:

vue
<template>
  <Input.Control class="data-[focused]:border-primary data-[state=invalid]:border-error" />
</template>
AttributeValuesComponents
data-statepristine, valid, invalidRoot, Control
data-dirtytrueRoot
data-focusedtrueRoot, Control
data-disabledtrueRoot, Control
data-readonlytrueRoot, Control

Accessibility

Input.Control renders as a native <input> and manages all ARIA attributes automatically.

ARIA Attributes

AttributeValueNotes
aria-invalidtrueWhen validation fails or error prop is set
aria-labelLabel textFrom Root’s label prop
aria-describedbyDescription IDOnly present when Input.Description is mounted
aria-errormessageError IDOnly present when Input.Error is mounted and errors exist
aria-requiredtrueFrom Root’s required prop
requiredtrueNative attribute, from Root’s required prop
disabledtrueNative attribute, from Root’s disabled prop
readonlytrueNative attribute, from Root’s readonly prop

Keyboard Navigation

Standard native <input> keyboard behavior. No custom key handlers — the browser handles focus, selection, and editing.

API Reference

The following API details are for all variations of the Input component.

Input.Root

Props

id

any

Unique identifier (auto-generated if not provided)

Default: useId()

label

string | undefined

Optional display label

name

string | undefined

Form field name

type

string | undefined

Input type

Default: "text"

form

string | undefined

Associate with form by ID

disabled

MaybeRefOrGetter<boolean> | undefined

Disables this input

Default: false

readonly

MaybeRefOrGetter<boolean> | undefined

Makes this input readonly

required

boolean | undefined

Whether this input is required

rules

any[] | undefined

Validation rules

Default: []

validateOn

ValidateOn | undefined

When to trigger validation

Default: "blur"

error

boolean | undefined

Manual error state override — forces invalid regardless of validation

Default: false

errorMessages

any

Manual error messages — merged with rule-based errors

namespace

string | undefined

Namespace for context provision to children

Default: "v0:input:root"

modelValue

string | undefined

Default: ""

Events

update:model-value

[value: string]

update:isFocused

[value: boolean]

Slots

default

InputRootSlotProps

Input.Control

Props

namespace

string | undefined

Default: "v0:input:root"

Slots

default

InputControlSlotProps

Input.Description

Props

id

string | undefined

Unique identifier (auto-generated if not provided)

Default: useId()

namespace

string | undefined

Namespace for connecting to parent Input.Root

Default: "v0:input:root"

Slots

default

InputDescriptionSlotProps

Input.Error

Props

id

string | undefined

Unique identifier (auto-generated if not provided)

Default: useId()

namespace

string | undefined

Namespace for connecting to parent Input.Root

Default: "v0:input:root"

Slots

default

InputErrorSlotProps
Was this page helpful?

© 2016-1970 Vuetify, LLC
Ctrl+/