Skip to main content

Introducing Workflow Engine, try for FREE workflowengine.io.

Form JSON

Form JSON is the blueprint for a form. It lists every component in a tree and adds the extra settings FormEngine Core needs to render, validate, and localize your form.

In this guide, you'll learn:

  • The shared top-level fields used by every form JSON
  • The common shape of component nodes
  • Where component-set-specific props come from
  • How RSuite and MUI forms look in practice

Shared form JSON fields

Form JSON is a single object. It combines metadata (actions, validation settings, tooltip/error/modal configuration, localization) with a root component tree in the form field. The top-level fields can include version, form, actions, formValidator, tooltipType, errorType, errorProps, modalType, localization, languages, and defaultLanguage.

Think of it like this: the top level is the form settings, and form is the tree of components to render.

note

Each component set ships component descriptions you can use to generate a JSON schema. The generated schema restricts which type values and props are allowed, while the rest of the structure stays shared.

Field reference (types and short descriptions):

FieldTypeDescription
versionPersistedFormVersionOptional schema version; when present, it must be "1".
formComponentStoreRoot component node for the form tree.
actionsActionValuesNamed code actions referenced by component events.
tooltipTypestringComponent type used for tooltips, such as MuiTooltip or RsTooltip.
errorTypestringComponent type used for validation errors, such as MuiErrorWrapper or RsErrorMessage.
errorPropsunknownAny JSON value passed to the error component.
modalTypestringComponent type used for modal rendering (for example MuiDialog or RsModal).
formValidatorstringJavaScript source string for the form-level validator function.
localizationLocalizationValueLocalization dictionary for component props.
languagesLanguage[]Supported languages metadata.
defaultLanguagestringDefault language code (for example en-US).

Here is a minimal top-level shape that renders a form without localization:

{
"tooltipType": "MuiTooltip",
"errorType": "MuiErrorWrapper",
"form": {
"key": "screen",
"type": "Screen",
"children": []
}
}

Add the other top-level fields when you need specific capabilities:

  • actions to define reusable code actions referenced by component events. See Actions and events.
  • formValidator to add form-level validation. See Validation.
  • localization, languages, and defaultLanguage to localize text and switch languages. See Localization.
  • tooltipType to select the tooltip wrapper component. See Tooltip.
  • errorType and errorProps to choose and configure the validation error component. See Validation.
  • modalType to select the modal component used for modal forms. See Custom modal components.

Actions live at the top level and are referenced by component events. The snippets below show only the relevant top-level fields; combine them with the minimal form above.

Actions

{
"actions": {
"logEmail": {
"body": " console.log('Email:', e.data.email)",
"params": {}
}
}
}

Reference actions from component events like this:

{
"key": "submit",
"type": "MuiButton",
"props": {
"children": {
"value": "Submit"
}
},
"events": {
"onClick": [
{
"name": "logEmail",
"type": "code"
}
]
}
}

Form-level validation

{
"formValidator": "const errors = {}; if (!formData.email) { errors.email = 'Required'; } return errors;"
}

Localization

{
"localization": {
"en-US": {
"email": {
"component": {
"label": "Email"
}
}
}
},
"languages": [
{
"code": "en",
"dialect": "US",
"name": "English",
"description": "American English",
"bidi": "ltr"
}
],
"defaultLanguage": "en-US"
}

Tooltip component

{
"tooltipType": "MuiTooltip"
}

Error component

{
"errorType": "RsErrorMessage",
"errorProps": {
"placement": {
"value": "bottomEnd"
}
}
}
{
"modalType": "MuiDialog"
}

Shared component node fields

Component nodes describe the UI elements in the form tree. Each node declares which component to render, its props, and any behavior tied to data, validation, or events.

If you are new to this format, focus on three fields first:

  • key: a unique id for the component
  • type: which UI component to render
  • props: the options for that component (label, placeholder, etc.)

Field reference (types and short descriptions):

FieldTypeDescription
keystringUnique component key in the form tree.
dataKeystringData key used for form data binding.
typestringComponent type name that must exist in the selected view.
propsRecord<string, ComponentProperty>Component props defined by the component set.
childrenComponentStore[]Child nodes (components) for layout or container components.
renderWhenComponentPropertyCondition that controls whether the component renders.
schemaBoundValueSchemaValidation rules for valued components.
eventsRecord<string, ActionData[]>Event-to-action mapping for this component.
tooltipPropsRecord<string, ComponentProperty>Props passed to the tooltip wrapper.
cssCssComponent-level CSS styles.
wrapperCssCssWrapper-level CSS styles.
styleComponentStyleInline styles for the component.
wrapperStyleComponentStyleInline styles for the wrapper.
slotstringTarget slot name in the parent component.

Here is a minimal component node that renders a basic field:

{
"key": "email",
"type": "MuiTextField",
"props": {
"label": {
"value": "Email"
}
},
"children": []
}

Add optional fields when you need specific capabilities. The snippets below show only the extra fields; combine them with the minimal node above.

Data binding

{
"dataKey": "email"
}

Validation

{
"schema": {
"validations": [
{
"key": "required"
}
]
}
}

Events

{
"events": {
"onBlur": [
{
"name": "validate",
"type": "common"
}
]
}
}

Conditional rendering

{
"renderWhen": {
"value": "form.data.subscribe === true"
}
}

Tooltip props

{
"tooltipProps": {
"title": {
"value": "We only use this to send updates"
}
}
}

CSS styling

{
"css": {
"any": {
"string": "margin-top: 8px;"
}
},
"wrapperCss": {
"any": {
"string": "padding: 8px;"
}
}
}

Inline styling

{
"style": {
"any": {
"string": "margin-top: 8px;"
}
},
"wrapperStyle": {
"any": {
"string": "padding: 8px;"
}
}
}

Slot

{
"slot": "title"
}

Advanced fields

  • htmlAttributes to attach HTML attributes (for example data-* or aria-*) to the rendered element.
  • modal to provide modal-specific props (and optional modal events) for modal components. See Custom modal components.
  • disableDataBinding to opt a component out of form data binding. See One-way data binding.

HTML attributes

{
"htmlAttributes": [
{
"data-testid": "email-field",
"aria-label": "Email"
}
]
}
{
"modal": {
"props": {
"title": {
"value": "Confirm"
}
}
}
}

Disable data binding

{
"disableDataBinding": {
"value": true
}
}

See the ComponentStore reference for the full list of optional fields.

Component-set-specific fields

The shared structure above stays the same, but props and type values come from the component set you are using. For example, MUI adds text-field-specific props on MuiTextField, while RSuite defines different input props for RsInput and other RSuite components.

Each entry in props is a ComponentProperty value, so it can be static, computed, or localized. Use the component set schema to see which props are available and which value shape is expected.

Component property value shapes:

Static value (use value):

{
"props": {
"label": {
"value": "Email"
}
}
}

Computed value (use computeType: "function" and fnSource). See Computed properties.

{
"props": {
"helperText": {
"computeType": "function",
"fnSource": "return form.data.email ? 'Looks good' : 'Enter your email';"
}
}
}

Localized value (use computeType: "localization" and add a matching entry in the top-level localization dictionary). See Localization.

{
"key": "email",
"type": "MuiTextField",
"props": {
"label": {
"computeType": "localization"
}
}
}
{
"localization": {
"en-US": {
"email": {
"component": {
"label": "Email"
}
}
}
}
}

Component-set-specific prop examples (non-localized):

{
"key": "email",
"type": "MuiTextField",
"props": {
"label": {
"value": "Email"
},
"helperText": {
"value": "name@example.com"
}
}
}
{
"key": "email",
"type": "RsInput",
"props": {
"label": {
"value": "Email"
},
"placeholder": {
"value": "name@example.com"
}
}
}

Examples

MUI form

This example uses the MUI view and MUI component types. It shows only what matters to compare MUI-specific props.

{
"tooltipType": "MuiTooltip",
"errorType": "MuiErrorWrapper",
"form": {
"key": "screen",
"type": "Screen",
"props": {},
"children": [
{
"key": "email",
"type": "MuiTextField",
"props": {
"label": {
"value": "Email"
},
"helperText": {
"value": "name@example.com"
}
}
},
{
"key": "password",
"type": "MuiTextField",
"props": {
"label": {
"value": "Password"
},
"type": {
"value": "password"
}
}
},
{
"key": "submit",
"type": "MuiButton",
"props": {
"children": {
"value": "Sign in"
}
}
}
]
}
}

Live example

Live Editor
function App() {
  const form = {
    tooltipType: 'MuiTooltip',
    errorType: 'MuiErrorWrapper',
    form: {
      key: 'screen',
      type: 'Screen',
      props: {},
      children: [
        {
          key: 'email',
          type: 'MuiTextField',
          props: {
            label: {
              value: 'Email'
            },
            helperText: {
              value: 'name@example.com'
            }
          }
        },
        {
          key: 'password',
          type: 'MuiTextField',
          props: {
            label: {
              value: 'Password'
            },
            type: {
              value: 'password'
            }
          }
        },
        {
          key: 'submit',
          type: 'MuiButton',
          props: {
            children: {
              value: 'Sign in'
            }
          }
        }
      ]
    }
  }

  const getForm = useCallback(() => JSON.stringify(form), [form])

  return <FormViewer view={muiView} getForm={getForm}/>
}
Result
Loading...

RSuite form

This example uses the RSuite view and RSuite component types. It shows only what matters to compare RSuite-specific props.

{
"tooltipType": "RsTooltip",
"errorType": "RsErrorMessage",
"form": {
"key": "screen",
"type": "Screen",
"props": {},
"children": [
{
"key": "email",
"type": "RsInput",
"props": {
"label": {
"value": "Email"
},
"placeholder": {
"value": "name@example.com"
}
}
},
{
"key": "password",
"type": "RsInput",
"props": {
"label": {
"value": "Password"
},
"type": {
"value": "password"
}
}
},
{
"key": "submit",
"type": "RsButton",
"props": {
"children": {
"value": "Sign in"
},
"appearance": {
"value": "primary"
}
}
}
]
}
}

Live example

Live Editor
function App() {
  const form = {
    tooltipType: 'RsTooltip',
    errorType: 'RsErrorMessage',
    form: {
      key: 'screen',
      type: 'Screen',
      props: {},
      children: [
        {
          key: 'email',
          type: 'RsInput',
          props: {
            label: {
              value: 'Email'
            },
            placeholder: {
              value: 'name@example.com'
            }
          }
        },
        {
          key: 'password',
          type: 'RsInput',
          props: {
            label: {
              value: 'Password'
            },
            type: {
              value: 'password'
            }
          }
        },
        {
          key: 'submit',
          type: 'RsButton',
          props: {
            children: {
              value: 'Sign in'
            },
            appearance: {
              value: 'primary'
            }
          }
        }
      ]
    }
  }

  const getForm = useCallback(() => JSON.stringify(form), [form])

  return <FormViewer view={viewWithCss} getForm={getForm}/>
}
Result
Loading...

Summary

  • Form JSON describes the component tree plus metadata for actions, tooltips, errors, and localization.
  • Shared top-level fields are consistent across component sets; component-specific props live under props.
  • Every component node follows the same base shape, with optional validation, events, and styling fields.
  • Use the component set schema to confirm which type values and props are supported.

For more information: