@mackehansson/schemaform

Validation

Use JSON Schema 2020-12 constraints, Ajv validation, and the renderable subset together.

Schemaform validates form data with JSON Schema draft 2020-12 using Ajv. In the domain language, JSON Schema is the data-and-validation layer of a Form Definition.

Validation constraints belong in JSON Schema:

  • required says which submitted properties must exist
  • type says what kind of value is valid
  • format handles values such as email addresses
  • minLength, maxLength, minItems, and maxItems define size limits
  • enum limits a value to a known set of choices

The UI Schema decides presentation. It can place a field on a Page, group Controls, choose a Widget such as textarea, or choose when errors are revealed. It does not decide whether a value is valid.

Interactive validation

The example starts with invalid values. Blur a field or submit the form to reveal its errors, then correct the value and the feedback clears as the Working State changes.

Live form

Sign up

Source

import type { FormDefinition } from "@mackehansson/schemaform";import { SchemaForm } from "@mackehansson/schemaform-shadcn";const signupForm = {  formatVersion: 1,  schema: {    type: "object",    required: ["email", "password"],    properties: {      email: {        type: "string",        title: "Email",        format: "email",      },      password: {        type: "string",        title: "Password",        minLength: 8,      },      bio: {        type: "string",        title: "Bio",        maxLength: 120,      },    },  },  uiSchema: {    revealErrors: "onBlur",    pages: [      {        type: "Page",        title: "Sign up",        children: [          { type: "Control", scope: "#/properties/email" },          { type: "Control", scope: "#/properties/password" },          {            type: "Control",            scope: "#/properties/bio",            widget: "textarea",          },        ],      },    ],  },  rules: [],} satisfies FormDefinition;export function App() {  return (    <SchemaForm      definition={signupForm}      onSubmit={(data) => console.log(data)}    />  );}

How errors surface

Core's Validator receives the JSON Schema and current Working State, then returns field errors keyed by JSON Pointer. The React hook merges those schema errors with any custom validators, tracks when errors should be revealed, and exposes helpers like errorsFor(pointer).

The Renderer decides how those errors look. In schemaform, each Widget receives the visible errors for its Control and renders them next to the input. The drop-in <SchemaForm> also blocks submit while visible fields are invalid; a blocked submit reveals all field errors.

You can control reveal behavior in the UI Schema or through a Renderer prop:

const definition = {
  formatVersion: 1,
  schema,
  uiSchema: {
    revealErrors: "onSubmit",
    pages: [
      {
        type: "Page",
        title: "Account",
        children: [{ type: "Control", scope: "#/properties/email" }],
      },
    ],
  },
  rules: [],
};

"onBlur" reveals a field's errors after that field is first blurred. "onSubmit" keeps field errors hidden until a submit attempt, or until gated navigation blocks the user on a page.

Renderable subset vs validation

JSON Schema is larger than any practical form renderer. Schemaform therefore has a documented Renderable Subset: the Form Builder only emits JSON Schema constructs that the Form Renderer knows how to display as Controls.

Validation is broader than rendering. Ajv still validates full JSON Schema 2020-12 with strict: false, even when a hand-written imported schema contains constructs outside the Renderable Subset. The Renderability Check is the tool that tells you whether a schema can be rendered as a form; the Validator tells you whether the current data satisfies the schema.

Use the distinction this way:

NeedUse
Validate submitted dataJSON Schema + Ajv
Decide which fields can be renderedRenderable Subset + Renderability Check
Choose the visual widget for a fieldUI Schema Control
Change required state based on Working StateRule

The rule of thumb from the three-layer model still applies: if it changes what data is valid, put it in JSON Schema. If it changes how a field appears, put it in UI Schema. If it changes behavior based on current data, put it in Rules.

On this page