UI Schemas
Overview
- UI Schema provides an “overlay” configuration for front-end presentation and form behavior of resource fields.
- It supports multi-layer overlays:
DEFAULT → TENANT → ROLE → USER, where the latter has higher priority and layers are merged in order. - Typical scenarios: customizing component type, default value, validation, and list/form visibility for fields under a given tenant, role, or user.
Structural conventions (overlay schemaJson)
- Top-level object contains
fields: a map keyed by field name. - Each field corresponds to a
FieldConfig, which contains UI attributes prefixed withx-.
Example:
{
"fields": {
"status": {
"x-component": "Select",
"x-options": ["Open", "Closed", "Pending"],
"x-default-value": "Open",
"x-label": "Status",
"x-visible": true,
"x-required": true
}
}
}
Notes:
- The merge strategy is deep-merge on
fields.<fieldName>by key. x-optionssupports two forms:- Simple array:
string[]as above (for quick choices) - Object:
{ choices: Array<Choice>, defaultValue?: any, ... }(commonly used for Select in some modules)
- Simple array:
Field-level UI attributes (FieldConfig)
The following x- attributes are commonly used across modules:
-
x-component: component type- Values (case-insensitive, shown here with capitalized examples):
Text,Textarea,Number,Boolean,Select,Date,DateTime,Email,Url - If omitted, the frontend may infer a default component based on the field’s data type.
- Values (case-insensitive, shown here with capitalized examples):
-
x-label: custom display label (string) -
x-default-value: default value (string | number | boolean | null)- Can be used as an alternative to
x-options.defaultValue; if both exist, the value from the higher-priority overlay wins.
- Can be used as an alternative to
-
x-visible: whether the field is visible (boolean) -
x-required: whether the field is required (boolean)- It may also be derived from the model’s
isNullable === false.
- It may also be derived from the model’s
-
x-tooltip: tooltip text (string) -
x-placeholder: placeholder text (string) -
x-options: component options (two forms)- Simple array:
string[](quick choices) - Object:
Record<string, any>, common fields:choices:Array<{ id: string; name: string } | string>defaultValue: default value at component levelmultiple:boolean(multi-select)- other component-specific props (e.g.
min/max/stepfor Number; date format for Date/DateTime, etc.)
- Simple array:
-
x-order: ordering weight (number)- Controls field order in lists/forms.
-
x-form-hidden: hide the field in forms (boolean) -
x-list-hidden: hide the field in list/table views (boolean)
Validation-related attributes (optional, component-dependent)
x-minLength: minimum length (number, forText/Textarea/Email/Url)x-maxLength: maximum length (number, forText/Textarea/Email/Url)x-min: minimum value (number, forNumber)x-max: maximum value (number, forNumber)x-email: email format validation (boolean, forEmail)
List filtering-related attributes (used in some apps)
x-filter-choices: filter options for list views (Array<{ id: string; name: string } | string>), used for building filter UI.
Merging and priority
- Overlay resolution order:
DEFAULT → TENANT → ROLE → USER. - Merge strategy: map deep-merge, arrays/scalars overwrite (later layer wins).
- Role level supports merging in the order of roles from the request to honor expected priority.
Overlay API
- Base path:
/metadata/ui-schema-overlays(gateway routes to backend controller/api/v1/ui-schema-overlays) - Supports pagination and filtering:
scopeType,tenantId,scopeKey,resourceName,page,pageSize - CRUD:
GET /,GET /{id},POST /,PUT /{id},DELETE /{id} - JSON validation:
GET /validate?json=<schemaJson>
Overlay object (UiSchemaOverlay)
Common fields:
scopeType:DEFAULT | TENANT | ROLE | USER(defaults toDEFAULT)scopeKey: scope key (ROLEuses role name,USERuses user ID)resourceId: entity ID (optional; you can also useresourceName)resourceName: entity name (optional; you can also useresourceId)schemaJson: overlay JSON as a string; when empty, the server can generate a default UI Schema from entity fieldstenantId: tenant ID (written by server based on request headers)
List overlays
- GET
/ui-schema-overlays - Permissions:
admin - Headers:
Authorization,X-Tenant-Id - Query params:
scopeType,scopeKey,resourceName(optional)page(starting from 1, default 1),pageSize(default 20)
Create overlay (with optional auto-generated schemaJson)
- POST
/ui-schema-overlays - Permissions:
admin - Headers:
Authorization,X-Tenant-Id
Rules:
- At least one of
resourceIdorresourceNamemust be provided (for locating the entity and generating default UI Schema). - When
schemaJsonis empty or blank, the server generates default schemaJson from entity fields. - When
schemaJsonis non-empty, it must be valid JSON orINVALID_JSONis returned.
Example:
curl -X POST \
"${API_BASE}/metadata/ui-schema-overlays" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Id: tenant-abc123" \
-H "Content-Type: application/json" \
-d '{
"scopeType": "TENANT",
"resourceName": "customers",
"schemaJson": "{ \"fields\": { \"email\": { \"x-component\": \"Email\", \"x-required\": true } } }"
}'
Update / delete / validate JSON
- PUT
/ui-schema-overlays/{id}: update overlay (requires valid JSON inschemaJson) - DELETE
/ui-schema-overlays/{id}: delete overlay - GET
/ui-schema-overlays/validate?json=<schemaJson>: verifyschemaJsonis valid JSON (200 if valid, 400 otherwise)
Examples
1. Select component (object-form x-options)
{
"fields": {
"category": {
"x-component": "Select",
"x-label": "Category",
"x-required": true,
"x-options": {
"choices": [
{ "id": "electronics", "name": "Electronics" },
{ "id": "books", "name": "Books" }
],
"multiple": false,
"defaultValue": "electronics"
}
}
}
}
2. Number component (range and placeholder)
{
"fields": {
"age": {
"x-component": "Number",
"x-label": "Age",
"x-required": true,
"x-min": 0,
"x-max": 120,
"x-placeholder": "Please enter age"
}
}
}
3. Simple array-form x-options (quick choices)
{
"fields": {
"status": {
"x-component": "Select",
"x-options": ["Open", "Closed", "Pending"],
"x-default-value": "Open"
}
}
}
Compatibility and recommendations
- Both forms of
x-optionsare supported; for richer props, use the object form. - To improve query performance, consider adding indexes on combinations like
(tenantId, scopeType, resourceName)on the backend. - Frontend editors can dynamically show appropriate attributes per component type (such as range and step for
Number, date format for Date/DateTime, etc.).
Unified UiSchema DSL (list / form / detail, etc.)
Besides the overlay schemaJson, the frontend also defines a unified UiSchema DSL for describing list pages, form pages, detail pages, related sub-tables, action buttons, and more. Core type definitions are in /ui/web/src/ui-schema-new/types.ts.
Top-level structure (UiSchema):
version: version string (for example"1.0")id: optional, ID of the schema itselfresource: resource name (for example"orders")i18n: i18n dictionarysecurity: security configurationroles: allowed rolespermissions: permission key/value pairs
defaults: global defaultsperPage: default page sizerowClick: list row click behavior (edit | show | expand | none)
list: list configuration (ListConfig)form: form configuration (FormConfig, includingcreate/edit)show: detail page configuration (ShowConfig)actions: action definitions (ActionDef[])views: predefined views (ViewDef[])metrics: embedded metric configurations (MetricDef[])
List configuration (ListConfig):
title: list titlecolumns: column definitions (ColumnDef[])source: field name
label: column titletype: column type (text/number/date, etc. as defined in frontend)sortable: whether sortablewidth: column widthformat: format string (for dates/numbers, etc.)component: cell component nameprops: props passed to the componentdefaultSort: default sort{ field, order }filters: filter definitions (FilterDef[])component: filter component (TextInput/NumberInput/BooleanInput/SelectInput/DatetimeInput/SearchInput/ReferenceInput)choices: local enum values (string/array/object)props: component props
filterDefaultValues: default filter valuestoolbar: toolbar actions{ actions: ActionRef[] }rowActions: per-row action listselection: selection configuration{ enabled: boolean }data: data source configuration (DataSourceDef)perPage: page size (fallback todefaults.perPageif missing)rowClick: row click behavior (fallback todefaults.rowClickif missing)
Form configuration (FormConfig):
create/edit: page configs (FormPage)FormPage:title: page titlelayout: sections (SectionDef[])submit: submit behavior (SubmitDef)
SectionDef:id: section IDtitle: section titlecols: total columns (12-grid)fields: list of form fields (FormFieldDef[])
FormFieldDef:source: field namelabel: labelcomponent: input component nameprops: component propsvisibleIf: visibility expressiondisabledIf: disabled expressionvalidate: validation rulesoptionsSource: options source (remote or localchoices)
Detail page configuration (ShowConfig):
title: detail titlelayout: section layout (ShowSectionDef[])toolbar: toolbar actionsrelated: related sub-table configs (RelatedDef[])
Related sub-table (RelatedDef):
id: sub-table IDtitle: titleresource: related resource nametarget: relation field (such as foreign key)filter: filter (JSON or template string)sort: sort configperPage: page sizecolumns: column definitionsdata: data source configuration
Action definition (ActionDef):
id: action ID (for reference)label: button labelicon: icon namekind: action type (link/dialog/mutation/download/custom)if: condition expressionconfirm: confirmation messagepayload: payload template (string or object)request: request definition (RequestDef)onSuccess/onError: action references after success/failure (ActionRef[])feedback: user feedback messages{ success, error }inputs: inputs to collect when triggering the action (ActionInputDef[])client: optional client-side behavior configuration (such as updating via dataProvider)
Request definition (RequestDef):
url: request URLmethod: HTTP method (defaultPOST)headers: request headersparamsTemplate: query parameter templatebodyTemplate: body template
Data source definition (DataSourceDef):
query:url: query URLmethod: HTTP methodparams: static query params
transform: optional transform expression or transformer name
Recommended approach:
- Use UiSchema DSL to describe page layout, actions, and data sources.
- Use UI Schema overlays for field-level overrides (component type, visibility, validations, etc.). These two mechanisms can be combined.