JSON Schema Form Customisation Guide
Altus uses a JSON Schema-driven form engine to render settings and configuration screens. This engine is built on the open-source react-jsonschema-form (RJSF) library, extended with Altus-specific custom widgets for Dataverse integration.
This guide explains how to use JSON Schema and UI Schema to customise existing Altus settings or define your own configuration forms.
Core Concepts
Every form is defined by two JSON documents:
| Document | Purpose |
|---|---|
Schema (schema.json) |
Defines the data structure, field types, validation rules, and default values. |
UI Schema (uiSchema.json) |
Controls how each field is rendered — which widget to use, layout options, and display behaviour. |
The schema determines what data is captured. The UI schema determines how it appears to the user.
For comprehensive documentation on the base JSON Schema capabilities, refer to the RJSF JSON Schema documentation.
Schema Basics
Field Types
JSON Schema supports the following base field types:
string— Text inputnumber— Decimal number inputinteger— Whole number inputboolean— True/false toggleobject— A group of named propertiesarray— A list of items
For full details, see the RJSF Single Fields documentation.
Simple Field Example
{
"type": "object",
"properties": {
"projectName": {
"type": "string",
"title": "Project Name"
},
"maxHours": {
"type": "integer",
"title": "Maximum Hours"
},
"isActive": {
"type": "boolean",
"title": "Active"
}
},
"required": ["projectName"]
}
This schema produces a form with a required text field, a number field, and a toggle.
Titles and Descriptions
Use title and description on any property to provide a label and helper text:
{
"projectName": {
"type": "string",
"title": "Project Name",
"description": "Enter the display name for this project."
}
}
Default Values
Set initial values with the default keyword:
{
"maxHours": {
"type": "integer",
"title": "Maximum Hours",
"default": 40
}
}
Enumerated Values
Restrict a field to a fixed set of choices using enum:
{
"priority": {
"type": "string",
"title": "Priority",
"enum": ["Low", "Medium", "High", "Critical"]
}
}
To provide custom display labels for enum values, use oneOf with const and title:
{
"status": {
"type": "number",
"title": "Status",
"oneOf": [
{ "const": 1, "title": "Draft" },
{ "const": 2, "title": "In Progress" },
{ "const": 3, "title": "Complete" }
]
}
}
For more on enumerations, see the RJSF Enumerated Values documentation.
Objects (Grouped Properties)
Group related fields together using object type:
{
"type": "object",
"title": "Notification Settings",
"properties": {
"emailEnabled": {
"type": "boolean",
"title": "Enable Email Notifications"
},
"recipientCount": {
"type": "integer",
"title": "Max Recipients"
}
}
}
For more, see the RJSF Objects documentation.
Arrays (Lists of Items)
Define repeating items with array type:
{
"milestones": {
"type": "array",
"title": "Milestones",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "Milestone Name"
},
"dueDate": {
"type": "string",
"title": "Due Date",
"format": "date"
}
}
}
}
}
For more, see the RJSF Arrays documentation.
Required Fields
Mark fields as mandatory using the required keyword on the parent object:
{
"type": "object",
"properties": {
"name": { "type": "string", "title": "Name" },
"email": { "type": "string", "title": "Email" }
},
"required": ["name", "email"]
}
UI Schema Basics
The UI Schema controls the visual presentation of each field. It mirrors the structure of the data schema and uses special ui: prefixed keys.
Assigning a Widget
Use ui:widget to specify which control renders a field:
{
"fieldName": {
"ui:widget": "EntityLookupWidget",
"ui:options": {
"endPoint": "sensei_projects",
"valueField": "sensei_projectid",
"nameField": "sensei_name"
}
}
}
Hiding a Field
{
"internalId": {
"ui:widget": "hidden"
}
}
Field Order
Control the display order of fields using ui:order:
{
"ui:order": ["name", "priority", "description", "*"]
}
The wildcard * places all remaining fields at that position.
Placeholder Text
{
"notes": {
"ui:placeholder": "Enter your notes here..."
}
}
Read-Only Fields
{
"createdBy": {
"ui:readonly": true
}
}
For the full list of standard UI Schema options, refer to the RJSF UI Schema documentation.
Altus Custom Widgets
In addition to the standard form controls, Altus provides custom widgets designed for Dataverse integration and specialised input scenarios. Use these by setting ui:widget to the widget name and passing configuration through ui:options.
Important
Widget names are case-sensitive. Always use the exact names listed below.
EntityLookupWidget
A single-record lookup that searches a Dataverse table. Displays a tag picker allowing the user to search and select one record.
Value stored: Record ID (GUID string) or null.
Required options:
| Option | Description |
|---|---|
endPoint |
Dataverse table logical collection name (e.g. sensei_projects) |
valueField |
The ID field of the target table (e.g. sensei_projectid) |
nameField |
The field used as the display label (e.g. sensei_name) |
Optional options:
| Option | Default | Description |
|---|---|---|
additionalSearchFilter |
— | Additional OData filter to narrow search results |
supportsContains |
true |
Whether the search supports contains-style filtering |
provider |
false |
Publishes the selected value for dependent fields (see Provider/Consumer Pattern) |
consumer |
— | Disables this field until the referenced provider field has a value |
Example:
{
"schema": {
"type": "object",
"properties": {
"project": {
"type": "string",
"title": "Project"
}
}
},
"uiSchema": {
"project": {
"ui:widget": "EntityLookupWidget",
"ui:options": {
"endPoint": "sensei_projects",
"valueField": "sensei_projectid",
"nameField": "sensei_name"
}
}
}
}
EntityLookupArrayWidget
A multi-select record lookup. Displays a tag picker allowing the user to search and select multiple records.
Value stored: Array of record IDs (GUID strings) or null.
Options: Same as EntityLookupWidget, plus:
| Option | Description |
|---|---|
readOnlyItems |
Array of record IDs that cannot be removed by the user |
Example:
{
"teamMembers": {
"ui:widget": "EntityLookupArrayWidget",
"ui:options": {
"endPoint": "systemusers",
"valueField": "systemuserid",
"nameField": "fullname"
}
}
}
OptionSetWidget
A dropdown populated from a Dataverse option set (choice column).
Value stored: Option set value as a string (e.g. "100000001").
Required options:
| Option | Description |
|---|---|
optionSetName |
The logical name of the global option set, or the attribute logical name for local option sets |
Optional options:
| Option | Default | Description |
|---|---|---|
entity |
none |
Required for local (entity-specific) option sets. Set to the table logical name. |
provider |
false |
Publishes the selected value for dependent fields |
consumer |
— | Disables this field until the referenced provider field has a value |
Example — Global option set:
{
"kpiField": {
"ui:widget": "OptionSetWidget",
"ui:options": {
"optionSetName": "sensei_overallprojectkpi"
}
}
}
Example — Local (entity-specific) option set:
{
"statusField": {
"ui:widget": "OptionSetWidget",
"ui:options": {
"optionSetName": "statuscode",
"entity": "incident"
}
}
}
CheckboxWidget
A toggle switch for boolean fields, rendered as a Fluent UI toggle control.
Value stored: true or false.
Optional UI Schema keys:
| Key | Default | Description |
|---|---|---|
offText |
No |
Label shown when the toggle is off |
onText |
Yes |
Label shown when the toggle is on |
Example:
{
"enableSync": {
"ui:widget": "CheckboxWidget",
"offText": "Disabled",
"onText": "Enabled"
}
}
CheckboxesWidget
A multi-select checkbox group for fields with enumerated values.
Value stored: Array of selected enum values.
Example:
Schema:
{
"enabledFeatures": {
"type": "array",
"title": "Enabled Features",
"items": {
"type": "string",
"enum": ["Reporting", "Notifications", "Approvals"]
},
"uniqueItems": true
}
}
UI Schema:
{
"enabledFeatures": {
"ui:widget": "CheckboxesWidget"
}
}
SelectWidget
A dropdown for fields with enumerated values, rendered as a Fluent UI dropdown.
Value stored: The selected enum value (or array of values for multi-select).
Example:
{
"priority": {
"ui:widget": "SelectWidget"
}
}
TextWidget
A text input that can optionally render as a combo box when the schema defines examples. Supports additional Fluent UI text field properties.
Value stored: String.
Example — Multiline text area:
{
"notes": {
"ui:widget": "TextWidget",
"ui:options": {
"props": {
"multiline": true,
"rows": 4,
"placeholder": "Add notes"
}
}
}
}
ReadOnlyTextWidget
Displays text as read-only. Supports inline bold formatting using **text** syntax within the value.
Value stored: String.
Example:
{
"instructions": {
"ui:widget": "ReadOnlyTextWidget"
}
}
A stored value of "Use **project defaults** for baseline setup." renders with "project defaults" in bold.
FormattedIntegerWidget
An integer input that displays a visual prefix or suffix while storing only the numeric value.
Value stored: Integer.
Optional options:
| Option | Description |
|---|---|
displayPrefix |
Text shown before the number (e.g. $) |
displaySuffix |
Text shown after the number (e.g. % or hrs) |
Example:
{
"hoursPerWeek": {
"ui:widget": "FormattedIntegerWidget",
"ui:options": {
"displaySuffix": " hrs"
}
}
}
ColourPickerWidget
A colour class selector used for capacity and legend colour coding. Presents a set of predefined colour classes.
Value stored: CSS class name string (e.g. altus-rh-capacity-level--4).
Note
This widget uses a fixed set of colour classes (altus-rh-capacity-level--0 through altus-rh-capacity-level--12). It is different from the built-in color widget.
Example:
{
"capacityColor": {
"ui:widget": "ColourPickerWidget"
}
}
Built-In Widgets
In addition to the Altus custom widgets above, the following standard widgets from the RJSF library are available:
| Widget | Description |
|---|---|
text |
Standard single-line text input |
textarea |
Multi-line text input |
checkbox |
Standard HTML checkbox |
color |
Browser colour picker |
hidden |
Hidden field (not displayed) |
Use built-in widgets when custom Dataverse integration or specialised rendering is not required.
Provider/Consumer Pattern
The provider/consumer pattern creates dependencies between fields. When a provider field has a value selected, dependent consumer fields become enabled. This is useful when one field's options depend on another field's selection.
How it works:
- Mark the source field with
"provider": truein itsui:options. - Mark the dependent field with
"consumer": "root_<providerFieldName>"in itsui:options. - The consumer field remains disabled until the provider field has a value.
Example:
{
"kpiField": {
"ui:widget": "EntityLookupWidget",
"ui:options": {
"provider": true,
"endPoint": "sensei_kpifields",
"valueField": "sensei_kpifieldid",
"nameField": "sensei_name"
}
},
"kpiValue": {
"ui:widget": "OptionSetWidget",
"ui:options": {
"consumer": "root_kpiField",
"optionSetName": "sensei_kpivalue"
}
}
}
In this example, the KPI Value dropdown is disabled until a KPI Field is selected.
Layout Extensions
Altus extends the standard RJSF layout with options to control field width and alignment.
Full Width
Force a field to span the full width of the form. This can be set in either the schema or the UI schema:
In the schema:
{
"description": {
"type": "string",
"fullWidth": true
}
}
Or in the UI schema:
{
"description": {
"fullWidth": true
}
}
Half Width
Keep a field at half width (the default for most fields):
{
"compactField": {
"halfWidth": true
}
}
Left-Aligned Row
Take a full row but keep the content left-aligned with whitespace on the right:
{
"sectionHeader": {
"leftAlignedRow": true
}
}
Array Presentation Options
Altus provides enhanced rendering options for array fields, controlled through the UI schema.
Dialog-Based Editing
Display array items in a summary table, with each item opening in a dialog for editing:
{
"initiativeTypes": {
"useArrayItemDialog": true,
"arrayTableColumns": ["table", "lookupField", "tabLabel"],
"noItemsText": "Select **Edit Defaults** to customise or add a new item."
}
}
| Option | Description |
|---|---|
useArrayItemDialog |
When true, array items are shown in a table with row editing via a dialog |
arrayTableColumns |
Array of property names controlling which columns appear and their order |
noItemsText |
Custom text shown when the array is empty (supports markdown) |
Inline Table Editing
Display array items directly in an editable table:
{
"evaluationPoints": {
"useArrayInlineTable": true,
"items": {
"useArrayInlineTableRow": true
}
}
}
| Option | Description |
|---|---|
useArrayInlineTable |
When true, renders the array as an inline editable table |
useArrayInlineTableRow |
Set on the items node to render object fields as table cells |
Complete Example
Below is a complete example combining schema and UI schema to create a settings form with Dataverse lookups, option sets, and layout controls.
Schema:
{
"type": "object",
"title": "Risk Configuration",
"properties": {
"riskCategory": {
"type": "string",
"title": "Risk Category"
},
"severity": {
"type": "string",
"title": "Severity"
},
"enableNotifications": {
"type": "boolean",
"title": "Enable Notifications",
"default": true
},
"maxRiskScore": {
"type": "integer",
"title": "Maximum Risk Score",
"default": 100
},
"notes": {
"type": "string",
"title": "Notes",
"fullWidth": true
},
"thresholds": {
"type": "array",
"title": "Risk Thresholds",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"title": "Label"
},
"minScore": {
"type": "integer",
"title": "Minimum Score"
},
"colour": {
"type": "string",
"title": "Colour"
}
},
"required": ["label", "minScore"]
}
}
},
"required": ["riskCategory"]
}
UI Schema:
{
"ui:order": ["riskCategory", "severity", "enableNotifications", "maxRiskScore", "notes", "thresholds"],
"riskCategory": {
"ui:widget": "EntityLookupWidget",
"ui:options": {
"endPoint": "sensei_riskcategories",
"valueField": "sensei_riskcategoryid",
"nameField": "sensei_name"
}
},
"severity": {
"ui:widget": "OptionSetWidget",
"ui:options": {
"optionSetName": "sensei_riskseverity"
}
},
"enableNotifications": {
"ui:widget": "CheckboxWidget",
"onText": "On",
"offText": "Off"
},
"maxRiskScore": {
"ui:widget": "FormattedIntegerWidget",
"ui:options": {
"displaySuffix": " pts"
}
},
"notes": {
"ui:widget": "TextWidget",
"ui:options": {
"props": {
"multiline": true,
"rows": 3,
"placeholder": "Additional notes..."
}
}
},
"thresholds": {
"useArrayInlineTable": true,
"items": {
"useArrayInlineTableRow": true,
"colour": {
"ui:widget": "ColourPickerWidget"
}
}
}
}
Quick Reference
Widget Summary
| Widget | Use Case | Value Type |
|---|---|---|
EntityLookupWidget |
Single Dataverse record lookup | GUID string |
EntityLookupArrayWidget |
Multi-select Dataverse record lookup | GUID string array |
OptionSetWidget |
Dataverse option set dropdown | Option value string |
CheckboxWidget |
Boolean toggle | Boolean |
CheckboxesWidget |
Multi-select checkboxes | Value array |
SelectWidget |
Enum dropdown | Enum value |
TextWidget |
Text input with optional combo box | String |
ReadOnlyTextWidget |
Read-only text display | String |
FormattedIntegerWidget |
Integer with display prefix/suffix | Integer |
ColourPickerWidget |
Colour class selector | CSS class string |
Common UI Schema Keys
| Key | Description |
|---|---|
ui:widget |
Specifies the widget to render |
ui:options |
Widget-specific configuration |
ui:order |
Controls field display order |
ui:placeholder |
Placeholder text |
ui:readonly |
Makes a field read-only |
ui:title |
Overrides the schema title |
ui:description |
Overrides the schema description |
fullWidth |
Forces full-width layout |
halfWidth |
Forces half-width layout |
leftAlignedRow |
Full row, left-aligned content |
Further Reading
- RJSF Documentation — JSON Schema — Comprehensive reference for all JSON Schema features supported by the form engine.
- RJSF Documentation — Single Fields — Field types, titles, enums, and validation.
- RJSF Documentation — Objects — Grouping properties, required fields, and field ordering.
- RJSF Documentation — Arrays — Array schemas and item definitions.
- RJSF Documentation — UI Schema — Full reference for all
ui:options.