Context
A Context is simply what the component exposes — roughly the same public instance you'd reach with a template ref. It holds the component's state, its resolved props, methods, event helpers and so on.
The Context differs per component. Every component's reference page has a Context tab listing precisely what its Context holds — see VfForm's for the form, or VfText's for a text element.
Form Context
useForm can be used to define a typed form — Schema plus any form-level props — in code, and returns a tuple of three:
const [MyForm, form$, formReady] = useForm({ schema })MyForm— the renderable component: essentiallyVfFormwith your options pre-bound.form$— the form's Context.formReady— a function that hands you that same Context once the form has mounted.
TIP
By convention, variables that hold a Context use the $ suffix in Vueform: form$ for a form, el$ for an element, context$ or ctx$ for generic ones. You'll see it throughout the docs.
form$ and formReady are two routes to the same: the form's Context. The Context has two layers. Its model members — data, validation, submission — work from the moment useForm returns, because the form builds its model engine up front. Its mounted members — element Contexts of rendered components, focus and scroll, steps and tabs — are undefined until the form mounts, and formReady waits for exactly that. Use form$ wherever reactivity absorbs the timing — like a template — and read it directly, no .value:
<script setup lang="ts">
import { defineSchema, useForm } from '@vueform/ui'
const schema = defineSchema({
email: { type: 'text', label: 'Email' },
})
const [MyForm, form$, formReady] = useForm({ schema })
</script>
<template>
<MyForm />
<p v-if="form$.submitting">Sending…</p>
</template>For code that must run against the mounted form, use formReady. Pass a callback, or call it with no arguments to get a promise of the same:
formReady((form$) => {
form$.find('email')
})
const form$ = await formReady()Element Context
The form Context is also the gateway to its elements: its find() method looks up any registered element's Context by Path. When the form is typed from a defineSchema result, TypeScript suggests the known paths:
import { defineSchema, useForm } from '@vueform/ui'
const schema = defineSchema({
name: { type: 'text', label: 'Name' },
country: {
type: 'select',
label: 'Country',
items: [
{ id: 'us', name: 'United States' },
{ id: 'de', name: 'Germany' },
],
},
})
const [MyForm, form$, formReady] = useForm({ schema })
formReady((form$) => {
form$.find('namecountry')
})What it returns is the matching element's Context — generics included:
formReady((form$) => {
form$.find('country')?.itemsitemsResolvedItem<{ id: string; name: string }>[]rawItemsresolveItems
})Unknown paths are still accepted: they return the element's Context typed as unknown. You get the same typing whether you reach the form through form$ or the formReady callback.
Context from Argument
You don't always have to go looking for a Context — sometimes it's handed to you. Every Event Handler receives the firing component's Context as its last argument. And wherever a Dynamic Prop accepts a function, that function is called with the owning component's Context, so the value can follow live state.
defineSchema({
name: {
type: 'text',
label: 'Name',
onChange: (newValue, oldValue, el$) => {
console.log(el$.valid)
},
help: (el$) => {
return el$.dirty ? 'Unsaved change' : ''
},
},
})useContext
Inside your own component rendered within a Vueform component or element — through a Slot or a Template — useContext returns the nearest Vueform component's Context: the same object the #template slot and event handlers receive. Its argument is the component's name, used purely for type inference:
import { useContext } from '@vueform/ui'
const el$ = useContext('VfText')See the useContext API page for full reference.