> ## Documentation Index
> Fetch the complete documentation index at: https://developer.uphold.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dynamic forms UI Schema reference

> Reference for the UI Schema that defines dynamic form layouts, controls, conditional visibility, and custom options based on the JSON Forms specification.

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](https://jsonforms.io/docs/uischema) 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

```json theme={null}
{
  "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.

```json theme={null}
{
  "type": "VerticalLayout",
  "elements": [
    { "type": "Control", "scope": "#/properties/firstName" },
    { "type": "Control", "scope": "#/properties/lastName" }
  ]
}
```

### HorizontalLayout

Arranges elements horizontally, side by side.

```json theme={null}
{
  "type": "HorizontalLayout",
  "elements": [
    { "type": "Control", "scope": "#/properties/firstName" },
    { "type": "Control", "scope": "#/properties/lastName" }
  ]
}
```

### Group

Groups related controls together with an optional label.

```json theme={null}
{
  "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.

```json theme={null}
{
  "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" }
      ]
    }
  ]
}
```

<Note>
  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.
</Note>

### ListWithDetail

Renders an array as a master-detail view — a list of items on one side and a detail form for the selected item.

```json theme={null}
{
  "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`.

```json theme={null}
{
  "type": "Control",
  "scope": "#/properties/email",
  "label": "Email Address"
}
```

| Property  | Description                                                               |
| --------- | ------------------------------------------------------------------------- |
| `scope`   | JSON Pointer to the schema property (e.g., `#/properties/email`)          |
| `label`   | Display label for the control (optional — derived from schema if omitted) |
| `options` | Custom options to modify control behavior                                 |

The renderer automatically selects the appropriate input type based on the schema:

| Schema Type                                | Rendered As          |
| ------------------------------------------ | -------------------- |
| `string`                                   | Text input           |
| `string` + `format: "date"` (schema)       | Date picker          |
| `string` + `options.format: "postal-code"` | Postal code input    |
| `string` + `enum`                          | Dropdown/select      |
| `number`                                   | Number input         |
| `boolean`                                  | Checkbox or toggle   |
| `array`                                    | List with add/remove |

## Rules

Rules control the visibility and enabled state of elements based on data conditions.

```json theme={null}
{
  "type": "Control",
  "scope": "#/properties/employerName",
  "rule": {
    "effect": "SHOW",
    "condition": {
      "scope": "#/properties/employmentStatus",
      "schema": { "const": "employed" }
    }
  }
}
```

### Effects

| Effect    | Description                                                  |
| --------- | ------------------------------------------------------------ |
| `SHOW`    | Show the element when condition is true, hide otherwise      |
| `HIDE`    | Hide the element when condition is true, show otherwise      |
| `ENABLE`  | Enable the element when condition is true, disable otherwise |
| `DISABLE` | Disable 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.

```json theme={null}
{
  "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](https://jsonforms.io/docs/uischema/controls#options).

### Array options

Options for array controls:

| Option             | Description                                          |
| ------------------ | ---------------------------------------------------- |
| `showSortButtons`  | Shows buttons to reorder items                       |
| `elementLabelProp` | Property to use as the label for each item in a list |
| `disableAdd`       | Prevents adding new items (renderer-specific)        |
| `disableRemove`    | Prevents removing items (renderer-specific)          |

```json theme={null}
{
  "type": "Control",
  "scope": "#/properties/documents",
  "options": {
    "elementLabelProp": "name",
    "showSortButtons": true
  }
}
```

### readonly

Renders the control as read-only.

```json theme={null}
{
  "type": "Control",
  "scope": "#/properties/accountId",
  "options": {
    "readonly": true
  }
}
```

### detail

For `ListWithDetail` layouts, specifies the UI Schema for the detail view.

```json theme={null}
{
  "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.

```json theme={null}
{
  "type": "Control",
  "scope": "#/properties/country",
  "options": {
    "data": {
      "source": "countries",
      "exclude": {
        "restrictions": [
          {
            "scope": "citizenship"
          }
        ]
      }
    }
  }
}
```

| Property                            | Description                                                                                                                                                                                                                                                               |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data.source`                       | The data source to fetch options from. <br />Available sources: `"countries"` ([List countries](/rest-apis/core-api/countries/list-countries)) and `"subdivisions"` ([Get country](/rest-apis/core-api/countries/get-country), returns subdivisions for a given country). |
| `data.exclude`                      | Optional filter to exclude values from the data source                                                                                                                                                                                                                    |
| `data.exclude.restrictions`         | Array of restrictions to filter values from the data source                                                                                                                                                                                                               |
| `data.exclude.restrictions[].scope` | The restriction scope (e.g., `citizenship`, `residence`, `geolocation`, `phone`)                                                                                                                                                                                          |

### format

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](/developer-guides/resources/dynamic-forms/schema#formats) and handled natively by renderers.

```json theme={null}
{
  "type": "Control",
  "scope": "#/properties/address/properties/postalCode",
  "options": {
    "format": "postal-code"
  }
}
```

| Format        | Description                                                                                                |
| ------------- | ---------------------------------------------------------------------------------------------------------- |
| `postal-code` | Renders 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.

<Note>
  These validation rules are defined in `options.rules` on individual controls and are distinct from the [visibility rules](#rules) (`rule` with `effect` and `condition`) that control element visibility.
</Note>

```json theme={null}
{
  "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[].rule`            | The validation rule type. Available rules: `difference-greater-than-or-equal-to-threshold`, `difference-less-than-or-equal-to-threshold`. |
| `rules[].threshold.limit` | The numeric limit for the threshold                                                                                                       |
| `rules[].threshold.unit`  | The 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.

```json theme={null}
{
  "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                                            |
| ------------------- | ------------------------------------------------------ |
| `dependsOn`         | Array of field dependencies                            |
| `dependsOn[].name`  | The logical name of the dependency (e.g., `"country"`) |
| `dependsOn[].scope` | JSON 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:

```json theme={null}
{
  "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

<CardGroup cols={2}>
  <Card title="Rendering" icon="code" href="./rendering">
    Implementation guidance and best practices.
  </Card>
</CardGroup>
