Globals
Some values belong to the whole app rather than to one form — a country list several selects share, a permission flag that should lock every form at once, a formatter used in many help texts. Globals are where these live. They are members you return from a globals hook in createVueform, readable from every Vueform component, handler, and plugin. They are not Configuration — config holds Vueform's own options and registries, while globals carry your values and your shared reactive state.
Defining Globals
Pass a globals hook to createVueform. It receives a GlobalsHookContext — the resolved config and a read-only view of the globals merged so far — and returns the members to add. Returning nothing is allowed.
import { ref } from 'vue'
import { createVueform } from '@vueform/ui/bundle'
app.use(
createVueform({
globals: () => ({
countries: ['United States', 'Germany', 'Japan'],
readonly: ref(false),
}),
})
)A member can be any value — data, functions, refs, computeds. Only ref and computed members are reactive. The Globals object itself is not, so assigning or adding members at runtime updates nothing — mutable state goes in a ref, and new members go through a hook.
Globals are built lazily, once per instance, the first time anything reads them — usually the first Vueform component's setup. Every form and component of every app the instance is installed into shares that one object. A global ref is therefore instance state, so on an SSR server the instance belongs in the per-request app factory — see Cross-Request State.
Typing Globals
The hook's return type is Partial<VueformGlobals>, so returning a member TypeScript doesn't know about is a compile error — and reading it back doesn't type-check either. Augmenting the interface is the first step of using globals in TypeScript, not an optional extra:
import type { Ref } from 'vue'
declare module '@vueform/core' {
interface VueformGlobals {
countries: string[]
readonly: Ref<boolean>
}
}Place the augmentation in any .d.ts file TypeScript includes. One augmentation types the hook's return and every read route — useGlobals(), $globals, and vueform.globals. Plugins ship theirs next to the plugin — see Augmenting Types.
Reading Globals
Three routes lead to the same object — which one to use depends on where your code runs.
In a Component
In the setup of any component inside an app that installed Vueform, useGlobals() returns the Globals:
<script setup lang="ts">
import { useGlobals } from '@vueform/ui'
const { readonly } = useGlobals()
</script>
<template>
<button @click="readonly = !readonly">
{{ readonly ? 'Unlock' : 'Lock' }} all forms
</button>
</template>Flipping the ref locks every consumer at once — the next section wires a field to it. useGlobals is setup-only and throws a NotInstalledError anywhere else; the two routes below cover those cases.
From a Context
Every Vueform component's Context carries the Globals as $globals, alongside $config, $i18n, and $http. Anything that receives a Context reads Globals without a setup — Event Handlers and Function Props, Functional Conditions, slot and template code:
{
country: {
type: 'select',
label: 'Country',
items: (el$) => el$.$globals.countries,
},
name: {
type: 'text',
label: 'Name',
readonly: (el$) => el$.$globals.readonly.value,
},
}This schema can live in its own module, outside any component. Refs are not unwrapped on $globals, so read them with .value. The readonly function re-evaluates whenever the ref changes — flip the global and the field locks live — Function Props cover why.
Outside Components
Pinia stores, router guards, and tests have neither a setup nor a Context. There, the instance returned by createVueform exposes the same object as vueform.globals:
vueform.globals.readonly.value = trueKeeping an instance reference at hand is covered in Outside a Setup Context.
Globals from Plugins
definePlugin accepts a globals hook too, with the same GlobalsHookContext argument and the same return contract:
import { definePlugin } from '@vueform/ui'
export default definePlugin(() => ({
name: 'feature-flags',
globals: () => ({
featureFlags: { newCheckout: true },
}),
}))Plugin hooks run after the app's hook, so a Plugin can read what the app defined through its globals argument and build on it. Registration and the rest of the plugin surface live in Plugins.
Merge Order and Overrides
Globals accumulate in a fixed order:
- Vueform's built-ins. The only one is
defaultBreakpoint, aComputedRef<string>resolving to'base'— the breakpoint under which the layout resolves unprefixed values. - The app's
globalshook. - Each plugin's hook in registration order — plugins bundled by the entry, then the
pluginsoption. Nestedpluginsrun depth-first, each right after its parent.
A name that already exists is overridden — the later value wins. In development, Vueform warns about each override (Global "x" is defined more than once — the later value wins), because two sources claiming one name is usually a collision. When the override is intentional, silence it with globalWarns: false in the config. Production never logs.
Leave defaultBreakpoint alone unless you mean to change the layout's base breakpoint, and keep your own member names distinct from it.