toRef
Get a
Readonly<Ref<T>>at any schema path, for the rare case a consumer surface expects a Vue ref instead of the values Proxy.
- Category
- Return method
- Signature
toRef(path) | toRef(segments)- Returns
Readonly<Ref<T>>- Reactive
- Yes
form.toRef(path) returns a Readonly<Ref<T>> whose .value tracks storage at path. For normal reads (templates, computeds, conditional rendering), form.values.<path> is the right call. toRef is for ref-shaped interop only.
const schema = z.object({
profile: z.object({ email: z.email() }),
todos: z.array(z.object({ title: z.string() })),
})
const form = useForm({ schema })
When to use it
External composables, watchers, and DevTools probes sometimes expect a Ref rather than a Proxy property:
const emailRef = form.toRef('profile.email') // Readonly<Ref<string>>
// Hand off to any third-party composable whose signature expects a Ref
useThirdPartyRefConsumer(emailRef)
// Watch a single path explicitly
watch(emailRef, (next) => {
/* respond to email changes */
})
Two call forms
toRef accepts either a dotted path or a segment tuple. Both resolve to the same leaf with the same inferred type:
form.toRef('profile.email')
form.toRef(['profile', 'email'])
The dotted form is shorter and idiomatic. The tuple form is the escape hatch for keys that contain a literal . (a dotted form can't disambiguate { 'a.b': … } from nested { a: { b: … } }). Pick whichever matches the surrounding code's grammar.
Read-only by contract
toRef returns Readonly<Ref<T>>. Writes go through the same paths every other consumer uses:
form.setValue('profile.email', 'a@b.c') // imperative write
form.register('profile.email') // bound writes via v-register
form.append('todos', { title: '' }) // structural writes
Attaform tracks dirty, touched, and validation state through those write paths. Assigning to .value directly throws; toRef is a read handle, not a backdoor.
Reactivity contract
toRef returns a ComputedRef-equivalent shape: reads inside reactive scopes track the path, and consumers re-run when storage at path changes. Two refs to the same path share reactivity; they don't double-subscribe.
const refA = form.toRef('profile.email')
const refB = form.toRef('profile.email')
// refA.value === refB.value, always.
// One storage write triggers both refs' subscribers.
The same path-precise reactivity Vue offers on form.values.profile.email, just wrapped in a Ref.