Code-Splitting
A schema names element types and Validation rules as strings — { type: 'text', rules: 'required|email' } — and strings resolve at runtime, so no bundler can look at your forms and work out which parts they use. Vueform makes the boundary explicit instead. What you register is what you bundle, and every registrable part — component, Module, rule, theme, locale — is importable on its own.
The Full Bundle
The Full Bundle is Vueform with nothing left to assemble. A single createVueform import from @vueform/ui/bundle registers everything — all components, Modules, and validation rules, the Default Theme, the en_US locale, and default adapters for HTTP, dates, and expressions.
Every size in this chapter is the gzipped amount the item adds to a production Vite build with default settings, measured with @vueform/ui@2.0.0-0.7 by the repository's size suite (pnpm size); all published numbers are for the regular virtual-DOM builds. In tables with a Total row, each row shows what removing that one item saves, while the Total is measured directly — items share code, so the Total can exceed the sum of the rows, and values near zero or slightly negative are compression noise. CSS only counts in builds that register the theme Module, so Module, rule, and locale rows are JS-only.
Modular Setup
A Modular Setup starts from the bare @vueform/ui entry, whose createVueform registers nothing by itself, and imports each part from its own subpath. This example registers everything, so every line of it is one thing you can leave out:
import { createApp } from 'vue'
import App from './App.vue'
import {
createVueform,
fetchHttpAdapter,
nativeDateAdapter,
pathExpressionAdapter,
} from '@vueform/ui'
import { en_US } from '@vueform/ui/locales'
import * as components from '@vueform/ui/components'
import * as modules from '@vueform/ui/modules'
import * as rules from '@vueform/ui/rules'
import { primary as theme } from '@vueform/ui/themes'
const app = createApp(App)
app.use(createVueform({
locales: { en_US },
theme,
components,
modules,
rules,
$http: { adapter: fetchHttpAdapter },
$date: { adapter: nativeDateAdapter },
$expression: { adapter: pathExpressionAdapter },
}))
app.mount('#app')Each subpath maps to one config option:
| Entry | Provides | Config option |
|---|---|---|
@vueform/ui/modules | The built-in Modules. | modules |
@vueform/ui/components | All components, each also importable from its own entry. | components |
@vueform/ui/rules | The built-in validation rules. | rules |
@vueform/ui/themes | The built-in Themes. | theme |
@vueform/ui/locales | The shipped Locales. | locales |
What the Bare Entry Leaves Out
The bare entry registers no components, Modules, rules, Theme, or Locale — and it also wires no adapters, the part of the example above that's easiest to forget. Each adapter matters once its Module is registered, and each costs only when wired:
- Without
$http.adapter, every request thehttpModule makes rejects with a "Nohttpadapter is configured" error. - Without
$date.adapter, thedateModule throws on first use. - Without
$expression.adapter, Expressions stay inert — even plain{path}interpolation — with a development warning.
All three defaults ship in @vueform/ui, as in the example. Swapping in custom ones is covered in Adapters and Plugins.
The Required Module
model is the one Module the bare entry can't run without. The bare createVueform's type doesn't accept a config without modules.model, and a config that slips one past TypeScript throws at createVueform(). The bare entry itself is 4.26 kB gzipped, and the smallest working build — core plus model — lands at 11.76 kB.
Only what's registered through the config needs these explicit choices. Anything you import by name — defineSchema, useForm, composables, helpers — tree-shakes like any ES Module.
Modules
Modules package Vueform's features — Validation, Conditions, HTTP, theming — as self-contained units, registered under modules:
import { createVueform } from '@vueform/ui'
import { model, validation, conditions } from '@vueform/ui/modules'
createVueform({
modules: { model, validation, conditions },
})Every Module except model is optional and degrades predictably when left out — see Leaving a Module Out for what each absence means.
| Module | Size |
|---|---|
model | 7.50 kB |
validation | 5.99 kB |
conditions | 2.01 kB |
autosave | 1.98 kB |
theme | 2.06 kB |
expression | 1.25 kB |
router | 0.59 kB |
http | 0.97 kB |
i18n | 0.28 kB |
security | 1.60 kB |
date | 0.14 kB |
| Total | 23.64 kB |
Components
A schema resolves its components by name through Vue's global registry — type: 'text' renders the component registered as VfText — so unused components only stay out of the bundle by not being registered. Cherry-pick them by importing each from its own entry:
import { createVueform } from '@vueform/ui'
import { model } from '@vueform/ui/modules'
import VfForm from '@vueform/ui/components/VfForm'
import VfElements from '@vueform/ui/components/VfElements'
import VfLayout from '@vueform/ui/components/VfLayout'
import VfAddon from '@vueform/ui/components/VfAddon'
import VfIcon from '@vueform/ui/components/VfIcon'
import VfText from '@vueform/ui/components/VfText'
createVueform({
modules: { model },
components: { VfForm, VfElements, VfLayout, VfAddon, VfIcon, VfText },
})@vueform/ui/components is the all-in barrel — import * as components registers everything, as in the full setup.
Importing a component's default entry brings in everything that component needs:
- The component — ready to register through
components. - Its stylesheets — exactly the chunks its markup needs, with chunks shared between components bundled once, so there are no CSS imports to manage; see Choosing a Theme for how styling ships.
- Its type registrations — the component joins Vue's
GlobalComponents, so it type-checks in templates without a local import, and an element joins theControlRegistry, so its schematypeand props —textforVfText— are known to schema-based forms.
When you want less than that, each entry has two leaner variants:
| Entry | Ships |
|---|---|
@vueform/ui/components/VfText | The component with its stylesheets. |
@vueform/ui/components/VfText/unstyled | The component without CSS, type registrations kept — for bringing your own styles. |
@vueform/ui/components/VfText/raw | No CSS and no global type registration (ControlRegistry, GlobalComponents). |
Under the vapor export condition every one of these entries resolves to its vapor build — see Vapor.
Internal Components
Components render each other by name too, so a trimmed set must include the internals your forms reach, not just the element types you write:
- Always —
VfFormrenders its schema throughVfElements, and elements render insideVfLayout, which rendersVfAddonandVfIcon. VfCheckboxesandVfRadiosrender aVfCheckbox/VfRadioper item.VfFile,VfList,VfMatrix, andVfNavrenderVfButton.- Multi-Page Forms need
VfStepsorVfTabs, plusVfNav. - Multilingual Inputs need
VfLanguages.
A component that's rendered but not registered surfaces as Vue's "Failed to resolve component" warning in development, with nothing rendered in its place.
Component Sizes
All components together add 49.88 kB of JS and 6.58 kB of CSS, both gzipped — each row below is what that component adds on top of the rest:
Elements
| Component | JS | CSS |
|---|---|---|
VfButton | 1.10 kB | 1.05 kB |
VfCheckbox | 0.75 kB | 0.00 kB |
VfCheckboxes | 0.77 kB | -0.01 kB |
VfContent | 0.26 kB | 0.04 kB |
VfField | 0.36 kB | 0.00 kB |
VfFile | 1.59 kB | 0.04 kB |
VfHidden | 0.27 kB | 0.00 kB |
VfRadio | 0.64 kB | 0.00 kB |
VfRadios | 0.59 kB | 0.00 kB |
VfSelect | 0.42 kB | 0.12 kB |
VfText | 0.42 kB | 0.09 kB |
VfTextarea | 0.46 kB | 0.04 kB |
Containers
| Component | JS | CSS |
|---|---|---|
VfForm | 5.77 kB | 0.00 kB |
VfGrid | 2.32 kB | 0.00 kB |
VfGroup | 0.47 kB | 0.08 kB |
VfList | 1.58 kB | 0.17 kB |
VfMatrix | 4.08 kB | 0.18 kB |
Form components
| Component | JS | CSS |
|---|---|---|
VfElements | 0.13 kB | 0.00 kB |
VfLanguages | 0.38 kB | 0.13 kB |
VfNav | 0.97 kB | 0.07 kB |
VfSteps | 0.17 kB | 0.63 kB |
VfTabs | 0.12 kB | 0.12 kB |
Layout & internals
| Component | JS | CSS |
|---|---|---|
VfAddon | 0.31 kB | 0.47 kB |
VfCol | 0.70 kB | 0.00 kB |
VfIcon | 0.13 kB | -0.01 kB |
VfLayout | 3.02 kB | 0.55 kB |
VfRow | 0.26 kB | 0.00 kB |
Rules
A rule referenced by name — rules: 'required|email' — must be registered under rules, or Validation throws naming the missing rule:
import { createVueform } from '@vueform/ui'
import { model, validation } from '@vueform/ui/modules'
import { required, email, min } from '@vueform/ui/rules'
createVueform({
modules: { model, validation },
rules: { required, email, min },
})Rules are the cheapest part of the framework. The complete built-in set adds 3.19 kB gzipped and a single rule costs between 0.17 kB and 0.60 kB, so import * as rules is a reasonable default — trimming pays off in the other sections first. Rules only run with the validation Module registered. Each rule's measured size is listed on its reference page, in the Bundle size row.
Theme
The cost of styling splits three ways. The theme Module (2.06 kB gzipped, in the Modules table) converts the Theme's Tokens to CSS variables and injects them. The Theme itself contributes its Token map, selected through the theme option — see Choosing a Theme:
import { createVueform } from '@vueform/ui'
import { model, theme } from '@vueform/ui/modules'
import { primary } from '@vueform/ui/themes'
createVueform({
modules: { model, theme },
theme: primary,
})| Tokens | Size |
|---|---|
primary | 3.95 kB |
material | 3.95 kB |
The third piece — the component stylesheets — ships with the components themselves and is already counted in the CSS column of the component tables. To leave it out and bring your own styles, import the components through their /unstyled entries.
Locales
The bare entry registers no Locale, so ship only the ones your app offers — how the active Locale is picked is covered in Registering Locales:
import { createVueform } from '@vueform/ui'
import { model, i18n } from '@vueform/ui/modules'
import { en_US, de_DE } from '@vueform/ui/locales'
createVueform({
modules: { model, i18n },
locales: { en_US, de_DE },
})| Code | Language | Size |
|---|---|---|
de_DE | German | 1.57 kB |
en_US | English (US) | 1.28 kB |
hu_HU | Hungarian | 1.72 kB |
Locale strings resolve through the i18n Module (0.28 kB gzipped, in the Modules table) — see Leaving a Module Out for what its absence means.
What the Full Bundle Costs
Registering everything, as the Full Bundle does, adds 92.83 kB gzipped to a production build — 86.25 kB of JS and 6.58 kB of CSS. That's the ceiling the sections above chip away at; the floor — core plus model — is 11.76 kB. Everything between the two is a choice you make per part.