values

A reactive Proxy keyed by your schema's paths. Drill anywhere, read anything, let Vue track it.

Category
Return property
Type
NestedReadType<Form>
Reactive
Yes

form.values is the reactive read surface for everything the form holds. The Proxy mirrors your schema's shape: every key resolves to a schema path, every container descends as its own sub-Proxy, and every read inside a reactive scope subscribes for re-renders. Reach for any leaf or container, anywhere; the value you read is always the live one. The exact concrete shape (how defaults, .optional(), .nullable(), and preprocess land) is covered in How values are stored.

form.values Demo Open in playground

Leaf reads

form.values.profile.firstName
""
form.values.profile.email
form.values.age
0

Container read

form.values.profile returns the whole nested object, live.

{
  "firstName": "",
  "lastName": ""
}

Whole form

form.values mirrors the schema root.

{
  "profile": {
    "firstName": "",
    "lastName": ""
  },
  "age": 0
}

Leaf and container reads

const schema = z.object({
  profile: z.object({
    name: z.string(),
    email: z.email(),
  }),
  age: z.number(),
})

const form = useForm({ schema })

// Leaf reads
form.values.profile.name
form.values.age

// Container reads return the nested object
form.values.profile // { name: '', email: '' }

Reactivity

form.values is implemented as a deep Proxy. Reads inside a computed, watchEffect, or template render are tracked; the consumer re-runs when the underlying storage changes:

<template>
  <p>Hello, {{ form.values.profile.name }}!</p>
</template>

Vue's auto-unwrap means you don't write .value; the Proxy presents as a plain object surface.

Reading in templates

form.values.<path> is a plain expression in templates, conditionals, and bindings:

<template>
  <button :disabled="!form.values.profile.email">Send invite</button>
  <p v-if="form.values.age >= 18">Adult plan available.</p>
  <span class="badge">Saving as {{ form.values.profile.firstName || 'guest' }}</span>
</template>

Each read subscribes the surrounding render so updates flow without manual watch or computed wiring.

Writes never go through the Proxy

form.values is read-only. Changes to storage flow through methods on form (setValue, clear, reset, resetField, and the field-array helpers append, prepend, insert, remove, swap, move, replace) or through inputs bound with v-register. Every write hits the same validation, dirty-tracking, and history pipeline; assigning to form.values.email directly throws in dev.

Optional leaves (those whose schema admits undefined, e.g. z.string().optional()) return to the absent state when the user clears the bound input. This keeps the .optional() semantic reachable from the DOM: a user who types invalid text into an optional field and then clears it sees storage flip back to undefined and the validation error clears too. Required leaves keep '' (or the slim default for non-strings) on clear.

Where to next