The UI Schema defines how to render a dynamic form — the layout structure, controls, conditional visibility rules, and custom options. It follows the JSON Forms UI Schema specification.
Structure
A UI Schema is a tree of elements . Each element has a type that determines how it behaves:
Layouts — Container elements that organize other elements
Controls — Elements that render input fields bound to schema properties
{
"type" : "VerticalLayout" ,
"elements" : [
{
"type" : "Control" ,
"scope" : "#/properties/firstName" ,
"label" : "First Name"
},
{
"type" : "Control" ,
"scope" : "#/properties/lastName" ,
"label" : "Last Name"
}
]
}
Layouts
Layouts organize controls and other layouts into a visual structure.
VerticalLayout
Arranges elements vertically, one below the other.
{
"type" : "VerticalLayout" ,
"elements" : [
{ "type" : "Control" , "scope" : "#/properties/firstName" },
{ "type" : "Control" , "scope" : "#/properties/lastName" }
]
}
HorizontalLayout
Arranges elements horizontally, side by side.
{
"type" : "HorizontalLayout" ,
"elements" : [
{ "type" : "Control" , "scope" : "#/properties/firstName" },
{ "type" : "Control" , "scope" : "#/properties/lastName" }
]
}
Group
Groups related controls together with an optional label.
{
"type" : "Group" ,
"label" : "Personal Information" ,
"elements" : [
{ "type" : "Control" , "scope" : "#/properties/firstName" },
{ "type" : "Control" , "scope" : "#/properties/lastName" }
]
}
Categorization
Organizes content into tabs or wizard-style steps. A Categorization contains multiple Category elements, each representing a step in the form.
{
"type" : "Categorization" ,
"elements" : [
{
"type" : "Category" ,
"label" : "Personal Info" ,
"elements" : [
{ "type" : "Control" , "scope" : "#/properties/personalInfo/properties/firstName" },
{ "type" : "Control" , "scope" : "#/properties/personalInfo/properties/lastName" }
]
},
{
"type" : "Category" ,
"label" : "Contact Info" ,
"elements" : [
{ "type" : "Control" , "scope" : "#/properties/contactInfo/properties/email" },
{ "type" : "Control" , "scope" : "#/properties/contactInfo/properties/phone" }
]
}
]
}
Categories correspond to steps in the schema. Each Category typically maps to a root-level schema property, enabling progressive disclosure as users complete each step.
ListWithDetail
Renders an array as a master-detail view — a list of items on one side and a detail form for the selected item.
{
"type" : "ListWithDetail" ,
"scope" : "#/properties/addresses" ,
"options" : {
"detail" : {
"type" : "VerticalLayout" ,
"elements" : [
{ "type" : "Control" , "scope" : "#/properties/street" },
{ "type" : "Control" , "scope" : "#/properties/city" }
]
}
}
}
Controls
Controls render input fields and bind them to schema properties.
Control
The Control element renders an input based on the schema type at the specified scope.
{
"type" : "Control" ,
"scope" : "#/properties/email" ,
"label" : "Email Address"
}
Property Description scopeJSON Pointer to the schema property (e.g., #/properties/email) labelDisplay label for the control (optional — derived from schema if omitted) optionsCustom options to modify control behavior
The renderer automatically selects the appropriate input type based on the schema:
Schema Type Rendered As stringText input string + format: "date" (schema)Date picker string + options.format: "postal-code"Postal code input string + enumDropdown/select numberNumber input booleanCheckbox or toggle arrayList with add/remove
Rules
Rules control the visibility and enabled state of elements based on data conditions.
{
"type" : "Control" ,
"scope" : "#/properties/employerName" ,
"rule" : {
"effect" : "SHOW" ,
"condition" : {
"scope" : "#/properties/employmentStatus" ,
"schema" : { "const" : "employed" }
}
}
}
Effects
Effect Description SHOWShow the element when condition is true, hide otherwise HIDEHide the element when condition is true, show otherwise ENABLEEnable the element when condition is true, disable otherwise DISABLEDisable the element when condition is true, enable otherwise
Conditions
A condition specifies a scope (the property to evaluate) and a schema that the value must match.
{
"condition" : {
"scope" : "#/properties/country" ,
"schema" : { "enum" : [ "US" , "CA" ] }
}
}
Common condition patterns:
Pattern Schema Equals a value { "const": "value" }One of many values { "enum": ["a", "b", "c"] }Not empty { "minLength": 1 }
Options
The options property on controls allows you to customize behavior. These are standard JSON Forms options .
Array options
Options for array controls:
Option Description showSortButtonsShows buttons to reorder items elementLabelPropProperty to use as the label for each item in a list disableAddPrevents adding new items (renderer-specific) disableRemovePrevents removing items (renderer-specific)
{
"type" : "Control" ,
"scope" : "#/properties/documents" ,
"options" : {
"elementLabelProp" : "name" ,
"showSortButtons" : true
}
}
readonly
Renders the control as read-only.
{
"type" : "Control" ,
"scope" : "#/properties/accountId" ,
"options" : {
"readonly" : true
}
}
detail
For ListWithDetail layouts, specifies the UI Schema for the detail view.
{
"type" : "ListWithDetail" ,
"scope" : "#/properties/items" ,
"options" : {
"detail" : {
"type" : "VerticalLayout" ,
"elements" : [ ... ]
}
}
}
Custom options
The Enterprise API Suite extends JSON Forms with custom options for specific use cases.
data
Specifies a data source for populating select options dynamically. The data object defines where to fetch options from and how to filter them.
{
"type" : "Control" ,
"scope" : "#/properties/country" ,
"options" : {
"data" : {
"source" : "countries" ,
"exclude" : {
"restrictions" : [
{
"scope" : "citizenship"
}
]
}
}
}
}
Property Description data.sourceThe data source to fetch options from. Available sources: "countries" (List countries ) and "subdivisions" (Get country , returns subdivisions for a given country). data.excludeOptional filter to exclude values from the data source data.exclude.restrictionsArray of restrictions to filter values from the data source data.exclude.restrictions[].scopeThe restriction scope (e.g., citizenship, residence, geolocation, phone)
Specifies custom display formats for controls that are not covered by the standard JSON Schema format keyword. Standard formats like date are defined in the JSON Schema and handled natively by renderers.
{
"type" : "Control" ,
"scope" : "#/properties/address/properties/postalCode" ,
"options" : {
"format" : "postal-code"
}
}
Format Description postal-codeRenders an input optimized for postal codes, with formatting and validation rules that may vary by country
rules (validation)
Defines client-side validation rules on a control. These rules allow you to enforce constraints that go beyond standard JSON Schema validation, such as age thresholds relative to the current date.
These validation rules are defined in options.rules on individual controls and are distinct from the visibility rules (rule with effect and condition) that control element visibility.
{
"type" : "Control" ,
"scope" : "#/properties/birthdate" ,
"options" : {
"rules" : [
{
"rule" : "difference-greater-than-or-equal-to-threshold" ,
"threshold" : {
"limit" : 18 ,
"unit" : "years"
}
},
{
"rule" : "difference-less-than-or-equal-to-threshold" ,
"threshold" : {
"limit" : 100 ,
"unit" : "years"
}
}
]
}
}
In this example, the date must be at least 18 years ago and at most 100 years ago, effectively restricting the input to valid adult birthday dates.
Property Description rules[].ruleThe validation rule type. Available rules: difference-greater-than-or-equal-to-threshold, difference-less-than-or-equal-to-threshold. rules[].threshold.limitThe numeric limit for the threshold rules[].threshold.unitThe unit for the threshold (e.g., "years")
dependsOn
Declares that a control depends on the value of one or more other controls. When a dependency value changes, the dependent control should update its available options or validation accordingly.
{
"type" : "Control" ,
"scope" : "#/properties/address/properties/subdivision" ,
"options" : {
"data" : {
"source" : "subdivisions"
},
"dependsOn" : [
{
"name" : "country" ,
"scope" : "#/properties/address/properties/country"
}
]
}
}
In this example, the subdivision field depends on the selected country — when the country changes, the list of available subdivisions is updated.
Property Description dependsOnArray of field dependencies dependsOn[].nameThe logical name of the dependency (e.g., "country") dependsOn[].scopeJSON Pointer to the dependency’s schema property
Dependencies can also be chained. For example, a postal code field can depend on both country and subdivision:
{
"type" : "Control" ,
"scope" : "#/properties/address/properties/postalCode" ,
"options" : {
"format" : "postal-code" ,
"dependsOn" : [
{
"name" : "country" ,
"scope" : "#/properties/address/properties/country"
},
{
"name" : "subdivision" ,
"scope" : "#/properties/address/properties/subdivision"
}
]
}
}
Next steps
Rendering Implementation guidance and best practices.