Open Web Components
On this page

Json Form#

An implementation of JsonForms built with lit and Web Awesome.

A Json Form allows you to create forms using only JSON. Every form needs two schemas to function:

All examples on this page will console-log their value every time they change.

Quick start#

export const simple = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value, ev)}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Please enter your name' },
        age: { type: 'integer', description: 'Please enter your age' },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        { type: 'Control', scope: '#/properties/name' },
        { type: 'Control', scope: '#/properties/age' },
      ],
    }}
  ></json-form>`;

Autofill presets#

Add options.autofill to a text control to keep free-text entry while offering common values. Use value for a single field or fill to update several form paths atomically. Combining multi: true with autofill renders a textarea.

export const autofill = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        iban: { type: 'string', title: 'IBAN' },
        bic: { type: 'string', title: 'BIC' },
        accountHolder: { type: 'string', title: 'Account holder' },
        internalNote: { type: 'string', title: 'Internal note' },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        {
          type: 'Control',
          scope: '#/properties/iban',
          options: {
            autofill: [
              {
                label: 'Main account · Example Bank',
                fill: {
                  '#/properties/iban': 'AT12 3456 7890',
                  '#/properties/bic': 'EXAMPLEAT',
                  '#/properties/accountHolder': 'Jane Doe',
                },
              },
            ],
          },
        },
        { type: 'Control', scope: '#/properties/bic' },
        { type: 'Control', scope: '#/properties/accountHolder' },
        {
          type: 'Control',
          scope: '#/properties/internalNote',
          options: {
            multi: true,
            autofill: [
              {
                label: 'Follow-up required',
                value: 'Please follow up with the customer.',
              },
            ],
          },
        },
      ],
    }}
    .value=${{}}
  ></json-form>`;

Controls#

These are the default Json-Form controls:

export const defaultControls = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        string: {
          type: 'string',
        },
        boolean: {
          type: 'boolean',
          description: 'Boolean description as a tooltip',
        },
        number: {
          type: 'number',
        },
        integer: {
          type: 'integer',
        },
        date: {
          type: 'string',
          format: 'date',
        },
        time: {
          type: 'string',
          format: 'time',
        },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        {
          type: 'Control',
          scope: '#/properties/string',
        },
        {
          type: 'Control',
          scope: '#/properties/boolean',
        },
        {
          type: 'Control',
          scope: '#/properties/number',
        },
        {
          type: 'Control',
          scope: '#/properties/integer',
        },
        {
          type: 'Control',
          scope: '#/properties/date',
        },
        {
          type: 'Control',
          scope: '#/properties/time',
        },
      ],
    }}
    .value=${{
      string: 'This is a string',
      boolean: true,
      number: 50.5,
      integer: 50,
      date: '2020-06-25',
    }}
  ></json-form>`;

Supported schema types:

Supported schema formats (type string):

Array#

The ArrayLayout renders one card per entry of an array property, with buttons to add and remove entries. It takes a scope pointing to the array property and a single layout in elements that is repeated for every entry. Scopes inside that layout are written as if they pointed at the array itself — the entry index is inserted automatically.

Supported options:

export const array = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        string: { type: 'string' },
        array: {
          type: 'array',
          minItems: 3,
          items: {
            type: 'object',
            properties: { string: { type: 'string' }, number: { type: 'number' } },
          },
        },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        { type: 'Control', scope: '#/properties/string' },
        {
          type: 'ArrayLayout',
          label: 'Array',
          scope: '#/properties/array',
          elements: {
            type: 'VerticalLayout',
            elements: [
              { type: 'Control', scope: '#/properties/array/properties/string' },
              { type: 'Control', scope: '#/properties/array/properties/number' },
              { type: 'Control', scope: '#/properties/string' },
            ],
          },
        },
      ],
    }}
    .value=${{
      string: 'foobar',
      array: [{ string: 'foo' }, { string: 'bar', number: 5 }],
    }}
  >
  </json-form>`;

Multiple choice#

There are two types that will produce multiple-choice inputs, enum and oneOf.

export const enumOneOf = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        enum: {
          type: 'string',
          enum: ['One', 'Two', 'Three'],
        },
        typeOneOf: {
          type: 'integer',
          title: 'oneOf',
          oneOf: [
            { const: 1, title: 'One' },
            { const: 2, title: 'Two' },
            { const: 3, title: 'Three' },
          ],
        },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        {
          type: 'Control',
          scope: '#/properties/enum',
        },
        {
          type: 'Control',
          scope: '#/properties/typeOneOf',
        },
      ],
    }}
  ></json-form>`;

Multiselect#

If you want to be able to select multiple options at once, make the property an array and set the items to enum or oneOf.

export const multiselect = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        enum: {
          type: 'array',
          items: {
            type: 'string',
            enum: ['One', 'Two', 'Three'],
          },
        },
        typeOneOf: {
          type: 'array',
          title: 'oneOf',
          items: {
            type: 'integer',
            oneOf: [
              { const: 1, title: 'One' },
              { const: 2, title: 'Two' },
              { const: 3, title: 'Three' },
            ],
          },
        },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        {
          type: 'Control',
          scope: '#/properties/enum',
        },
        {
          type: 'Control',
          scope: '#/properties/typeOneOf',
        },
      ],
    }}
  ></json-form>`;

Options#

export const options = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        toggle: {
          type: 'boolean',
        },
        checkboxTag: {
          type: 'boolean',
        },
        tagVariant: {
          type: 'boolean',
        },
        size: {
          type: 'boolean',
        },
        slider: {
          type: 'number',
          minimum: 1,
          maximum: 50,
        },
        rating: {
          type: 'number',
          minimum: 0,
          maximum: 5,
        },
        multi: {
          type: 'string',
        },
        radio: {
          type: 'string',
          enum: ['One', 'Two', 'Three'],
        },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        {
          type: 'Control',
          scope: '#/properties/toggle',
          options: {
            toggle: true,
          },
        },
        {
          type: 'Control',
          scope: '#/properties/checkboxTag',
          options: {
            checkboxTag: true,
          },
        },
        {
          type: 'Control',
          scope: '#/properties/tagVariant',
          options: {
            checkboxTag: true,
            tagVariant: 'success',
          },
        },
        {
          type: 'Control',
          scope: '#/properties/size',
          label: 'Large Checkbox',
          options: {
            size: 'large',
          },
        },
        {
          type: 'Control',
          scope: '#/properties/slider',
          options: {
            slider: true,
          },
        },
        {
          type: 'Control',
          scope: '#/properties/rating',
          options: {
            rating: true,
          },
        },
        {
          type: 'Control',
          scope: '#/properties/multi',
          options: {
            multi: true,
          },
        },
        {
          type: 'Control',
          scope: '#/properties/radio',
          options: {
            format: 'radio',
          },
        },
      ],
    }}
  ></json-form>`;

Supported uiSchema options:

Layouts#

To structure your forms, you can put your controls into different layouts. A layout is an entry in the uiSchema with type set to ...Layout and a list of elements.

Vertical layout#

export const vertical = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Please enter your name' },
        age: { type: 'integer', description: 'Please enter your age' },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        { type: 'Control', scope: '#/properties/name' },
        { type: 'Control', scope: '#/properties/age' },
      ],
    }}
  ></json-form>`;

Horizontal layout#

export const horizontal = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Please enter your name' },
        age: { type: 'integer', description: 'Please enter your age' },
      },
    }}
    .uiSchema=${{
      type: 'HorizontalLayout',
      elements: [
        { type: 'Control', scope: '#/properties/name' },
        { type: 'Control', scope: '#/properties/age' },
      ],
    }}
  ></json-form>`;

Group layout#

A group layout is like a vertical layout, but it has a visual separator and an additional label property.

export const group = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Please enter your name' },
        age: { type: 'integer', description: 'Please enter your age' },
      },
    }}
    .uiSchema=${{
      type: 'GroupLayout',
      label: 'Group Layout',
      elements: [
        { type: 'Control', scope: '#/properties/name' },
        { type: 'Control', scope: '#/properties/age' },
      ],
    }}
  ></json-form>`;

Nested layouts#

Elements in Layouts can also be other layouts

export const nested = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Please enter your name' },
        age: { type: 'integer', description: 'Please enter your age' },
      },
    }}
    .uiSchema=${{
      type: 'GroupLayout',
      label: 'Nested Layout',
      elements: [
        {
          type: 'HorizontalLayout',
          elements: [
            {
              type: 'VerticalLayout',
              elements: [
                { type: 'Control', scope: '#/properties/name' },
                { type: 'Control', scope: '#/properties/age' },
              ],
            },
            {
              type: 'VerticalLayout',
              elements: [
                { type: 'Control', scope: '#/properties/name' },
                { type: 'Control', scope: '#/properties/age' },
              ],
            },
          ],
        },
      ],
    }}
  ></json-form>`;

CheckboxCombo layout#

A horizontal layout meant for checkboxes with tags that adds a 'select-all' checkbox

export const checkboxCombo = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        size: {
          type: 'object',
          properties: {
            small: { type: 'boolean' },
            medium: { type: 'boolean' },
            large: { type: 'boolean' },
          },
        },
      },
    }}
    .uiSchema=${{
      type: 'CheckboxComboLayout',
      elements: [
        {
          type: 'Control',
          scope: '#/properties/size/properties/small',
          options: {
            checkboxTag: true,
            tagVariant: 'neutral',
          },
        },
        {
          type: 'Control',
          scope: '#/properties/size/properties/medium',
          options: {
            checkboxTag: true,
            tagVariant: 'brand',
          },
        },
        {
          type: 'Control',
          scope: '#/properties/size/properties/large',
          options: {
            checkboxTag: true,
            tagVariant: 'success',
          },
        },
      ],
    }}
  ></json-form>`;

Tab Layout#

A layout for grouping forms into tabs.

export const tabs = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Please enter your name' },
        age: { type: 'integer', description: 'Please enter your age' },
      },
    }}
    .uiSchema=${{
      type: 'GroupLayout',
      label: 'Tab Layout',
      elements: [
        {
          type: 'TabLayout',
          options: {
            tabNames: ['foo', 'bar'],
          },
          elements: [
            {
              type: 'VerticalLayout',
              elements: [{ type: 'Control', scope: '#/properties/name' }],
            },
            {
              type: 'VerticalLayout',
              elements: [{ type: 'Control', scope: '#/properties/age' }],
            },
          ],
        },
      ],
    }}
  ></json-form>`;

Details Layout#

A layout for hiding forms.

export const details = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        street: { type: 'string' },
        nr: { type: 'string' },
        city: { type: 'string' },
        postalCode: { type: 'string' },
      },
    }}
    .uiSchema=${{
      type: 'GroupLayout',
      label: 'Details Layout',
      elements: [
        {
          type: 'DetailsLayout',
          label: 'My Address',
          options: {
            formatter: '{street} {nr}, {postalCode} {city}',
            formatterScope: '#/properties',
          },
          subLayout: {
            type: 'VerticalLayout',
            elements: [
              { type: 'Control', scope: '#/properties/street' },
              { type: 'Control', scope: '#/properties/nr' },
              { type: 'Control', scope: '#/properties/postalCode' },
              { type: 'Control', scope: '#/properties/city' },
            ],
          },
        },
      ],
    }}
    .value=${{
      street: 'Broadway',
      nr: '2',
      postalCode: '10000',
      city: 'New York',
    }}
  ></json-form>`;

Labels#

The third type of uiSchema elements is labels. Labels can be used to divide the form into multiple parts, while still keeping it linear. Controls can also be given labels. If a control has no label, it will try to generate it from the property name.

export const labels = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value, ev)}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Please enter your name' },
        age: { type: 'integer', description: 'Please enter your age' },
        fruit: {
          type: 'string',
          oneOf: [
            { const: 'apple', title: 'Apples' },
            { const: 'orange', title: 'Oranges' },
          ],
        },
      },
    }}
    .uiSchema=${{
      type: 'GroupLayout',
      label: 'Layout Label',
      elements: [
        { type: 'Control', scope: '#/properties/name', label: 'Name: also this is a custom label' },
        { type: 'Control', scope: '#/properties/fruit', label: 'Your favorite fruit' },
        { type: 'Label', scope: '#/properties/fruit' },
        { type: 'Label', text: 'Are you old enough?' },
        { type: 'Control', scope: '#/properties/age' },
      ],
    }}
  ></json-form>`;

Separator#

You can add a separator by using type separator.

export const separator = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value, ev)}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Please enter your name' },
        age: { type: 'integer', description: 'Please enter your age' },
      },
    }}
    .uiSchema=${{
      type: 'GroupLayout',
      label: 'Layout Label',
      elements: [
        { type: 'Control', scope: '#/properties/name', label: 'Name: also this is a custom label' },
        { type: 'Separator', options: { type: 'horizontal', label: 'I am a Separator' } },
        { type: 'Control', scope: '#/properties/age' },
      ],
    }}
  ></json-form>`;

Rules#

Controls and layouts can be shown, hidden, enabled and disabled dynamically with rules. A rule has an effect (SHOW, HIDE, ENABLE or DISABLE) and a condition with a scope pointing at a property of the form value and a schema that is validated against that property. The effect is applied while the condition matches.

export const rules = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        employed: { type: 'boolean' },
        employer: { type: 'string' },
        salary: { type: 'number' },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        { type: 'Control', scope: '#/properties/employed', options: { toggle: true } },
        {
          type: 'Control',
          scope: '#/properties/employer',
          rule: {
            effect: 'SHOW',
            condition: { scope: '#/properties/employed', schema: { const: true } },
          },
        },
        {
          type: 'Control',
          scope: '#/properties/salary',
          rule: {
            effect: 'ENABLE',
            condition: { scope: '#/properties/employed', schema: { const: true } },
          },
        },
      ],
    }}
  ></json-form>`;

Validation#

The form value is validated against the schema on every change. Errors are shown on a control once the user has interacted with it — set forceErrors to show all errors immediately (e.g. when the user tries to submit an untouched form).

export const validation = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.validatorState)}
    forceErrors
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string' },
        email: { type: 'string', minLength: 5 },
      },
      required: ['name'],
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        { type: 'Control', scope: '#/properties/name' },
        { type: 'Control', scope: '#/properties/email' },
      ],
    }}
    .value=${{ email: 'a@b' }}
  ></json-form>`;

The form element also exposes:

Note: empty strings, null and empty objects are stripped from the value before validation, so an empty required text field reports a "required" error instead of a type error.

Readonly#

Set the readonly attribute to disable all controls of a form. ArrayLayout add/remove buttons are hidden as well.

export const readonly = () =>
  html`<json-form
    readonly
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string' },
        age: { type: 'integer' },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        { type: 'Control', scope: '#/properties/name' },
        { type: 'Control', scope: '#/properties/age' },
      ],
    }}
    .value=${{ name: 'James Bond', age: 42 }}
  ></json-form>`;

Getting the form data#

Every time the user changes the form, a formDataChange event is fired on the json-form element. The current change is accessible via the value and path properties, but you probably want the full form value, which is in the value property of the form element (which is the event target).

export const events = () =>
  html`<json-form
    @formDataChange=${ev =>
      console.log(
        `Changed property: ${ev.path}, New value: ${ev.value}, New form value:`,
        ev.target.value,
      )}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string' },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [{ type: 'Control', scope: '#/properties/name' }],
    }}
  ></json-form>`;

Switching renderers#

You can switch to clickEditable renderers midway through a uiSchema.

export const switchRenderers = () =>
  html`<json-form
    @formDataChange=${ev =>
      console.log(
        `Changed property: ${ev.path}, New value: ${ev.value}, New form value:`,
        ev.target.value,
      )}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string' },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        { type: 'VerticalLayout', elements: [{ type: 'Control', scope: '#/properties/name' }] },
        {
          type: 'VerticalLayout',
          elements: [
            { type: 'Control', scope: '#/properties/name' },
            { type: 'VerticalLayout', elements: [{ type: 'Control', scope: '#/properties/name' }] },
          ],
          options: { renderers: 'clickEditable' },
        },
      ],
    }}
  ></json-form>`;

Other options#

Next to schema and uiSchema, Json-Form also supports these properties:

The rootForm and validatorState properties are managed internally by the form — don't set them.

Overwriting and adding Renderers#

Changing the renderers is still experimental and not very well supported. The default render types are:

If you set one of these, it will overwrite the existing renderer. Otherwise, a new one will be added. You can then use your new renderer by setting the name of your render type to true in the options of the uiSchema.

import { inputListener, resolveDataSchema, processLabel } from '@open-wc/components/JsonForm.js';

export const customize = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        name: { type: 'string' },
        acceptTerms: { type: 'boolean', description: 'Accept Terms?' },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        { type: 'Control', scope: '#/properties/name' },
        { type: 'Control', scope: '#/properties/acceptTerms', options: { dontClickAway: true } },
      ],
    }}
    .renderers=${{
      // don't use these renderers, they are just examples
      dontClickAway: (state, ruleOptions, value) =>
        html`<input
            @input=${inputListener(state.uiSchema, 'checked')}
            ?checked=${resolveDataSchema(value, state.uiSchema.scope)}
            type="radio"
          /><label>${processLabel(state)}</label>`,
      string: (state, ruleOptions, value) =>
        html`<div contenteditable="true" @input=${inputListener(state.uiSchema, 'innerText')}>
          ${resolveDataSchema(value, state.uiSchema.scope)}
        </div>`,
    }}
    .value=${{ name: 'James Bond', acceptTerms: false }}
  ></json-form>`;

Displaying schemas#

Set mode to 'schema' to render the shape of the data — the field types — instead of interactive inputs. Useful for read-only previews or documenting a schema.

export const schemaMode = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .mode=${'schema'}
    .schema=${{
      type: 'object',
      properties: {
        string: {
          type: 'string',
        },
        boolean: {
          type: 'boolean',
          description: 'Boolean description as a tooltip',
        },
        number: {
          type: 'number',
        },
        integer: {
          type: 'integer',
        },
        date: {
          type: 'string',
          format: 'date',
        },
        time: {
          type: 'string',
          format: 'time',
        },
      },
    }}
    .uiSchema=${{
      type: 'VerticalLayout',
      elements: [
        {
          type: 'Control',
          scope: '#/properties/string',
        },
        {
          type: 'Control',
          scope: '#/properties/boolean',
        },
        {
          type: 'Control',
          scope: '#/properties/number',
        },
        {
          type: 'Control',
          scope: '#/properties/integer',
        },
        {
          type: 'Control',
          scope: '#/properties/date',
        },
        {
          type: 'Control',
          scope: '#/properties/time',
        },
      ],
    }}
    .value=${{
      string: 'This is a string',
      boolean: true,
      number: 50.5,
      integer: 50,
      date: '2020-06-25',
    }}
  ></json-form>`;

Array Tabs Kitchen Sink#

A larger combined example: an ArrayLayout nested inside a TabLayout, showing how layouts, controls, and array entries compose together in a single form.

export const arrayTabsKitchenSink = () =>
  html`<json-form
    @formDataChange=${ev => console.log(ev.target.value)}
    .schema=${{
      type: 'object',
      properties: {
        string: { type: 'string' },
        number: { type: 'number' },
        array: {
          type: 'array',
          minItems: 3,
          items: {
            type: 'object',
            properties: { string: { type: 'string' }, number: { type: 'number' } },
          },
        },
      },
    }}
    .uiSchema=${{
      type: 'TabLayout',
      options: { tabNames: ['foo', 'array'] },
      elements: [
        {
          type: 'VerticalLayout',
          elements: [
            { type: 'Control', scope: '#/properties/string' },
            { type: 'Control', scope: '#/properties/number' },
          ],
        },
        {
          type: 'VerticalLayout',
          elements: [
            {
              type: 'ArrayLayout',
              label: 'Array',
              scope: '#/properties/array',
              elements: {
                type: 'VerticalLayout',
                elements: [
                  { type: 'Control', scope: '#/properties/array/properties/string' },
                  { type: 'Control', scope: '#/properties/array/properties/number' },
                  { type: 'Control', scope: '#/properties/string' },
                ],
              },
            },
          ],
        },
      ],
    }}
    .value=${{
      string: 'foobar',
      array: [
        { string: 'foo', number: 4 },
        { string: 'bar', number: 5 },
      ],
    }}
  >
  </json-form>`;

API#

Attributes & properties#

PropertyTypeDefaultDescription
schemaJsonSchema7{}JSON Schema that defines and validates the form data.
uiSchemaUISchemaElement{}JsonForms UI schema that selects controls, layouts, labels, and options.
valueobject{}Current form data. Updated in place as controls emit formDataChange.
validatorStateValidationResult{ valid: true, errors: [] }Latest validation result for value against schema.
rootFormbooleantrueMarks the form that owns value updates and validation. Nested forms set this internally.
forceErrorsbooleanfalseForces validation errors to render even before a field has been touched.
renderersRendererRecord{}Custom renderers merged with the default renderer set.
readonlybooleanfalseRenders controls read-only by passing readonly into control options.
mode'form' | 'schema''form'Selects the form rendering mode.

Events#

EventDescription
formDataChangeFired by controls and layouts when form data changes; read the current form data from target.value.

Methods#

MethodDescription
validate()Revalidates value against schema and updates validatorState.
getFirstInvalid()Returns the first invalid nested json-form element, or undefined.