Form Schema vs. VeeValidate and FormKit
VeeValidate and FormKit are both built around describing your form's structure in the component template — you write <Field>/<FormKit> per field, and logic (validation, show/hide, dependent options) hangs off that markup. That's a good fit when the form is known ahead of time and doesn't change at runtime.
Form Schema solves a different problem — a form whose structure is itself data: it arrives from the server, gets assembled from a CMS, depends on the user's role, or on the previous wizard step's answer. Instead of markup, an array of FieldDefinition; instead of a template, useForm(schema), which can render on top of that schema however you like — headless with your own markup, or ui-themes for a ready-made one.
Where this actually matters
- The form changes at runtime.
visible/disabled/optionson aFieldDefinitionaccept functions of the form's current values — a field decides whether to show itself before you'd ever write av-ifin a template. In VeeValidate/FormKit the same logic is component-template code you write fresh for every such form. - The schema comes from the server. If a backend form builder returns JSON, it maps directly onto a
FieldDefinition[]— nobody hand-generates a component with conditional markup for 30 fields. - SSR with no workarounds.
useForm()doesn't touch the DOM on init — the form's state exists independently of wherever it eventually gets rendered. - Not locked into one validation schema.
validators/asyncValidatorsare plain functions, but the same field can just as easily wrap an existing Zod/Yup/Valibot schema — no need to relearn validation if the project already has one of those in place.
Where VeeValidate/FormKit are still the more natural fit
If a form is static and known at write time — a login form, a contact form, a 5-field checkout — writing a FieldDefinition[] instead of markup is often just an extra layer of indirection. Form Schema is built for forms that change, not as a wholesale replacement for form markup for its own sake.