Skip to main content
Vuetify0 is now a release candidate!
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

createInput

Shared form field primitive with validation state, ARIA ID generation, and automatic form registration.

Usage

ts
import { createInput } from '@vuetify/v0'
import { ref } from 'vue'

const value = ref('')
const input = createInput({
  value,
  rules: [v => !!v || 'Required'],
})

// Field state
input.isDirty.value      // false (no content)
input.isPristine.value   // true (unchanged)
input.isTouched.value    // false (not interacted)

// Trigger validation
await input.validate()
input.isValid.value      // false
input.errors.value       // ['Required']
input.state.value        // 'invalid'

// Update value
value.value = 'hello'
input.isDirty.value      // true
input.isPristine.value   // false

// Reset
input.reset()
value.value              // '' (initial value)
input.isPristine.value   // true
input.isValid.value      // null (unvalidated)

Architecture

Diagram

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

Generic Types

createInput is generic over the value type:

ts
// String (default) — for text inputs
createInput({ value: ref('') })

// Number | null — for numeric inputs
createInput<number | null>({
  value: ref<number | null>(null),
  dirty: v => v !== null,
  equals: (a, b) => Object.is(a, b),
})

// ID | ID[] — for select inputs
createInput<ID | ID[]>({
  value: ref<ID[]>([]),
  dirty: v => Array.isArray(v) ? v.length > 0 : v != null,
})

Reactivity

PropertyTypeDescription
valueRef<T>The field value (same ref passed in)
isDirtyReadonly<Ref<boolean>>Has content (via dirty predicate)
isPristineReadonly<Ref<boolean>>Unchanged since mount/reset
isFocusedShallowRef<boolean>Writable — component sets on focus/blur
isTouchedShallowRef<boolean>Writable — component sets after first interaction
isDisabledReadonly<Ref<boolean>>Resolved from disabled option
isReadonlyReadonly<Ref<boolean>>Resolved from readonly option
errorsReadonly<Ref<string[]>>Merged validation + manual errors
isValidReadonly<Ref<boolean | null>>Tri-state: null (unvalidated), true, false
isValidatingReadonly<Ref<boolean>>Async validation in progress
stateReadonly<Ref<InputState>>'pristine' | 'valid' | 'invalid'
MethodDescription
validate()Run rules, returns Promise<boolean>
reset()Restore initial value, clear validation
Tip

isDirty and isPristine are not inverses. A pre-filled form field is dirty AND pristine. A cleared field is not-dirty AND not-pristine.

Examples

Build Your Own Text Field

A reusable TextField component built on createInput, then dropped into a profile form twice. Each instance calls createInput in its own setup, so the two fields track validation, dirty, pristine, touched, and focused state completely independently — the composable carries no shared module state. createInput deliberately binds no DOM events, so TextField wires onFocus and onBlur itself: focus flips isFocused, blur sets isTouched and calls validate(). That keeps the “when do I validate” policy in the component where it belongs.

The reusable field also wires the accessibility surface createInput hands it: aria-describedby points at input.errorId while there are errors and falls back to input.descriptionId for the hint, and aria-invalid mirrors isValid === false. Both IDs are generated once and stable across re-renders. The compact flag row underneath makes the field-state model concrete — note that dirty and pristine are independent, so a pre-filled field reads as both at once while a cleared field reads as neither. TextField exposes validate, reset, and isValid via defineExpose, which lets the entry coordinate the whole form: submit validates every field through its template ref before saving, and reset restores each field’s initial value and clears its validation.

Reach for this pattern when you want full control over a field’s markup and styling but do not want to re-implement validation, state tracking, and ARIA wiring by hand. If you only need rule evaluation without the field-state layer, use createValidation directly; if the value is numeric, createNumberField adds Intl formatting and min/max/step on top; and if you would rather not build the shell at all, the Input component packages this exact composable into a ready-made compound surface.

FileRole
useProfile.tsOwns the demo state (name, email, saved) and the per-field validation rules
TextField.vueReusable field built on createInput — wires focus/blur, ARIA, and reset
profile-form.vueEntry that renders two fields and coordinates submit/reset via template refs

Your full name

dirtypristinetouchedfocused

We'll never share it

dirtypristinetouchedfocused

FAQ

Discord
Need help? Join our community for support and discussions ↗

API Reference

The following API details are for the createInput composable.
Was this page helpful?

Ctrl+/