setValue patterns
The programmatic write surface: three call shapes, a callback option, and the
unsetsentinel for flagging any path blank.
- Category
- Return method
- Signatures
setValue(value) · setValue(path, value) · setValue(segments, value)- Callback form
(prev, ctx) => next- Returns
boolean
Click the four buttons in the demo to exercise every setValue shape: string path, segment tuple, callback, whole-form. The reactive surface (values, fields, validation) updates the same way it does for directive-driven writes; setValue and v-register share the pipeline. The Three call shapes section unpacks each form.
Three call shapes
setValue accepts whatever shape fits the write site. Each example below assumes a form handle from useForm({ schema }):
const form = useForm({ schema })
form.setValue({ name: 'Ada', age: 30 }) // whole-form
form.setValue('profile.email', 'a@b.c') // dotted path
form.setValue(['profile', 'email'], 'a@b.c') // segment tuple
The dotted-path form is the most ergonomic for plain object schemas. The segment-tuple form sidesteps the dotted-key collision (a schema key that contains a literal .) and gives TypeScript precise typed-tuple inference. The whole-form shape replaces every path at once: useful for hydrating from a server response or applying external state.
Callback form
Pass a function to read the current value and return the next:
form.setValue('count', (prev) => (prev ?? 0) + 1)
form.setValue('tags', (prev) => [...prev, 'new-tag'])
form.setValue(['profile', 'name'], (prev) => prev.trim().toLowerCase())
The callback receives the current value at the path; its return value lands in storage. Equivalent to reading form.values.<path>, computing the next value, and writing it, in one atomic step.
Returns boolean
setValue returns true when the write was accepted, false when it was rejected by the slim-type gate (value didn't match the leaf's accept set). Reach for the return value when a downstream action depends on the write succeeding:
if (form.setValue('age', 21)) {
/* proceed */
}
Same pipeline as v-register
Every setValue call flows through the same write pipeline as the directive: slim-type gating, dirty / touched tracking, persistence, multi-tab sync, history. The reactive surface (values, fields, validation) reacts identically. No "programmatic writes are second-class" carve-out.
The one difference: setValue writes are never coerced. Coercion is for user-typed DOM strings; values you pass to setValue are already typed at the call site (TypeScript checks it), so coercion would be a no-op at best and a footgun at worst.
Flagging a path blank
To flag a path as displayed-empty, pass the unset sentinel. The runtime writes the schema's slim value at that path and joins the path to form.blankPaths so the bound input renders empty.
import { unset } from 'attaform/zod'
form.setValue('middleName', unset) // storage holds '', form.blankPaths has 'middleName'
form.setValue('profile', unset) // recursive, marks every primitive descendant
form.setValue('cargo', unset) // DU stub, writes { kind: '' } with no variant body
unset works at every path the consumer can address: primitive leaves, containers, arrays, tuples, records, discriminated unions, wrappers, and the root. See the unset page for the full contract.
Where to next
reset&resetField: restore defaults instead of just writing.clear: write blank values without going through defaults.- Field-array mutations: append, insert, remove, swap, move, replace, prepend.
unset: the blank-anywhere sentinel fordefaultValues,setValue, andreset.