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.
Field reference (types and short descriptions):
| Field | Type | Description |
|---|---|---|
| version | PersistedFormVersion | Optional schema version; when present, it must be "1". |
| form | ComponentStore | Root component node for the form tree. |
| actions | ActionValues | Named code actions referenced by component events. |
| tooltipType | string | Component type used for tooltips, such as MuiTooltip or RsTooltip. |
| errorType | string | Component type used for validation errors, such as MuiErrorWrapper or RsErrorMessage. |
| errorProps | unknown | Any JSON value passed to the error component. |
| modalType | string | Component type used for modal rendering (for example MuiDialog or RsModal). |
| formValidator | string | JavaScript source string for the form-level validator function. |
| localization | LocalizationValue | Localization dictionary for component props. |
| languages | Language[] | Supported languages metadata. |
| defaultLanguage | string | Default 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:
actionsto define reusable code actions referenced by component events. See Actions and events.formValidatorto add form-level validation. See Validation.localization,languages, anddefaultLanguageto localize text and switch languages. See Localization.tooltipTypeto select the tooltip wrapper component. See Tooltip.errorTypeanderrorPropsto choose and configure the validation error component. See Validation.modalTypeto 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"
}
}
}
Modal component
{
"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):
| Field | Type | Description |
|---|---|---|
| key | string | Unique component key in the form tree. |
| dataKey | string | Data key used for form data binding. |
| type | string | Component type name that must exist in the selected view. |
| props | Record<string, ComponentProperty> | Component props defined by the component set. |
| children | ComponentStore[] | Child nodes (components) for layout or container components. |
| renderWhen | ComponentProperty | Condition that controls whether the component renders. |
| schema | BoundValueSchema | Validation rules for valued components. |
| events | Record<string, ActionData[]> | Event-to-action mapping for this component. |
| tooltipProps | Record<string, ComponentProperty> | Props passed to the tooltip wrapper. |
| css | Css | Component-level CSS styles. |
| wrapperCss | Css | Wrapper-level CSS styles. |
| style | ComponentStyle | Inline styles for the component. |
| wrapperStyle | ComponentStyle | Inline styles for the wrapper. |
| slot | string | Target 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.
dataKeyto bind the component to a specific data key (defaults tokey). See Handling form data.schemato add per-component validation rules. See Validation.eventsto trigger actions from component events. See Actions and events.renderWhento conditionally render a component. See Conditional rendering.tooltipPropsto enable per-component tooltips. See Tooltip.cssandwrapperCssto apply CSS rules. See Styling components and forms.styleandwrapperStylefor inline styles. See Styling components and forms.slotto target named slots in composite components. See Embedded forms.
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
htmlAttributesto attach HTML attributes (for exampledata-*oraria-*) to the rendered element.modalto provide modal-specific props (and optional modal events) for modal components. See Custom modal components.disableDataBindingto opt a component out of form data binding. See One-way data binding.
HTML attributes
{
"htmlAttributes": [
{
"data-testid": "email-field",
"aria-label": "Email"
}
]
}
Modal settings
{
"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
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}/> }
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
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}/> }
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: