Views
Metadata schema for defining generated UI views in SolidX.
View Metadata
Where it lives
JSON Pointer: /views
JSONPath: $.views
Parent: Root of the metadata document
Overview
View metadata defines how SolidX presents models and fields to end users.
If field metadata answers "what does this field mean?", view metadata answers "how should this field appear here?"
This page is the primary reference for:
- top-level view records
- layout attrs
- field-node attrs inside layouts
- widget selection through
editWidgetandviewWidget - widget-specific attrs
- real metadata examples for
form,list, andtreerendering
For field semantics, validation intent, persistence behavior, and field-level attrs, see Field Metadata.
How To Read This Page
SolidX has multiple view families, but not all of them need the same kind of documentation.
formandlistare the primary homes for field widget documentationtreereuses the same field-column widgets aslistcardandkanbanare driven more by card composition and card widgets than by per-field widget catalogs
That is why the field list is repeated here by view family. The field contract lives on the field metadata page; the rendering contract lives here.
Common View Shape
Every view record follows the same top-level pattern:
{
"name": "application-list-view",
"displayName": "Applications",
"type": "list",
"context": "{}",
"moduleUserKey": "merchant-onboarding",
"modelUserKey": "application",
"layout": {
"type": "list",
"attrs": {},
"children": []
}
}View Metadata Attributes
| Attr | Purpose |
|---|---|
name | Internal identifier for the view record |
displayName | User-facing label for the view |
type | View family such as form, list, tree, card, or kanban |
context | Optional serialized context used by the view |
moduleUserKey | Module that owns the view |
modelUserKey | Model rendered by the view |
layout | The actual layout tree, including layout attrs and child nodes |
onFieldChange | Optional field-change workflow hooks for form-driven experiences |
onFormLayoutLoad | Optional hooks that run when a form layout loads |
List View
Use list when users need a tabular view over many records.
Common list Layout Attrs
| Attr | Purpose |
|---|---|
pagination | Enables paginated browsing |
pageSizeOptions | Controls the selectable page sizes |
enableGlobalSearch | Enables global search across searchable columns |
create | Enables create actions from the list screen |
edit | Enables edit actions from the list screen |
delete | Enables delete actions from the list screen |
rowButtons | Adds custom row-level actions |
headerButtons | Adds custom list-level actions |
allowedViews | Lets users switch between compatible view families |
truncateAfter | Truncates text-like cell output after a character count and shows the full value in the cell tooltip |
Common list Field-Node Attrs
These attrs belong to the layout node, not to the field metadata entity:
| Attr | Purpose |
|---|---|
name | Chooses the field to render |
label | Overrides the default column header |
sortable | Enables sorting for the column |
filterable | Enables column-level filtering |
isSearchable | Includes the field in global search flows |
viewWidget | Selects a custom list or tree widget |
shortText
By default, shortText renders as a standard text column.
The default list renderer is DefaultTextListWidget, which is used automatically when no custom viewWidget is set. This default path also respects the list-level truncateAfter attr, so it is the main way to control truncation for long textual values in list and tree views.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "bankUserId",
"isSearchable": true
}
}| Widget | Use when | Extra attrs |
|---|---|---|
MaskedShortTextListViewWidget | The column should hide the underlying value and show a masked version instead | None |
SolidShortTextAvatarWidget | The column should display an avatar-style badge next to the value | initialsKey when the row value is object-shaped |
SolidShortTextFieldImageListWidget | The stored value is an image URL and the column should render a thumbnail | None |
MaskedShortTextListViewWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget simply replaces the visible value with a same-length masked string. |
{
"type": "field",
"attrs": {
"name": "paymentGatewayAccessSecret",
"viewWidget": "maskedShortTextList",
"sortable": true,
"filterable": true,
"isSearchable": true
}
}SolidShortTextAvatarWidget
| Attr | Purpose |
|---|---|
initialsKey | When the rendered row value is an object rather than a plain string, this attr tells the widget which nested property should be used to compute the visible label and avatar initials. |
Example coming soon..
SolidShortTextFieldImageListWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget treats the field value as an image URL, renders a thumbnail, and opens the image in the built-in lightbox on click. |
Example coming soon..
longText
In list and tree views, longText reuses the standard text-column path.
Unlike form rendering, longText does not currently have dedicated list or tree widgets of its own. It behaves like a text column and relies on the same shared truncation and search mechanics as other text-style scalar fields.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget |
| Widget selection | Applied automatically through the shared text-column path |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "description"
}
}No additional list or tree widgets are currently registered specifically for longText.
The supported alternative widgets for longText are form-specific and are documented under Form View > longText below.
richText
In list and tree views, richText reuses the standard text-column path.
It does not currently have dedicated list or tree widgets of its own. In these views, it behaves like a text column and relies on the same shared truncation and search mechanics as other text-style scalar fields.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget |
| Widget selection | Applied automatically through the shared text-column path |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "content",
"label": "Content",
"sortable": true,
"filterable": true
}
}No additional list or tree widgets are currently registered specifically for richText.
json
Core form rendering is supported for json, but list and tree support is currently limited.
The core list-column dispatch does not currently include a dedicated json renderer, so JSON fields should not be assumed to have the same out-of-the-box list support as scalar text fields.
| Concern | How it works |
|---|---|
| Default widget | No dedicated core list widget is currently wired for json |
| Widget selection | No core json list-column path is currently registered in list dispatch |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | None specific to json at the core list level |
Show example from metadata
{
"type": "field",
"attrs": {
"name": "deviceRecognitionVerdict"
}
}No dedicated core list or tree widgets are currently registered specifically for json.
int
By default, int renders as a numeric column that reuses the shared text-style cell widget while preserving numeric table alignment.
The core list-column path routes int through SolidIntColumn, which applies numeric cell styling and then renders values through DefaultTextListWidget unless a custom viewWidget is provided.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget through the shared numeric column path |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "pageCount",
"label": "Page Count"
}
}No additional core list or tree widgets are currently registered specifically for int.
bigint
In list and tree views, bigint currently reuses the same numeric column path used for int.
The core list-column path routes bigint through SolidBigintColumn, which delegates to SolidIntColumn. As a result, bigint receives numeric table alignment but still renders through DefaultTextListWidget by default.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget through the shared numeric column path |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "id"
}
}No additional core list or tree widgets are currently registered specifically for bigint.
decimal
By default, decimal renders as a numeric column that reuses the shared text-style cell widget while preserving numeric table alignment.
The core list-column path routes decimal through SolidDecimalColumn, which delegates to the same numeric column implementation used for int. This means decimal values inherit the standard text-cell rendering behavior unless a custom viewWidget is provided.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget through the shared numeric column path |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "latePaymentFees",
"sortable": true,
"filterable": true,
"isSearchable": true
}
}No additional core list or tree widgets are currently registered specifically for decimal.
boolean
By default, boolean renders as a compact true-or-false column.
The default list renderer is DefaultBooleanListWidget, which turns the stored boolean value into readable output such as Yes and No. This default path is also where column-level label overrides such as trueLabel and falseLabel are applied.
| Concern | How it works |
|---|---|
| Default widget | DefaultBooleanListWidget |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable, trueLabel, falseLabel |
| Relevant layout attrs | enableGlobalSearch when the list experience needs a global search bar, though boolean fields are typically more useful in filters than in keyword search |
Show default example
{
"type": "field",
"attrs": {
"name": "isDuplicate",
"trueLabel": "Duplicate",
"falseLabel": "Not-Duplicate"
}
}No additional core list or tree widgets are currently registered specifically for boolean.
date
By default, date renders as a date-formatted column rather than as plain text.
The default list renderer is DefaultDateListWidget, which delegates value formatting to the shared date-view component. At the list level, formatting is controlled through the layout format attr.
| Concern | How it works |
|---|---|
| Default widget | DefaultDateListWidget |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | format, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "doj",
"sortable": true
}
}No additional core list or tree widgets are currently registered specifically for date.
datetime
By default, datetime renders as a formatted date-time column rather than as plain text.
The default list renderer is DefaultDateTimeListWidget, which delegates value formatting to the shared date-view component with time display enabled. At the list level, formatting is controlled through the layout format attr.
| Concern | How it works |
|---|---|
| Default widget | DefaultDateTimeListWidget |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | format, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "startTime",
"format": "DD/MM/YYYY HH:mm:ss"
}
}No additional core list or tree widgets are currently registered specifically for datetime.
time
In list and tree views, time currently reuses the standard text-column path.
The core list-column dispatch sends time fields through a dedicated column wrapper, but that wrapper falls back to DefaultTextListWidget. As a result, time currently behaves more like a text field in list and tree views than like a dedicated time-only renderer.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget through the shared text-column path |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable, format |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "startTime",
"format": "DD/MM/YYYY HH:mm:ss"
}
}No additional core list or tree widgets are currently registered specifically for time.
email
In list and tree views, email reuses the standard text-column path.
The core list-column dispatch sends email fields through the same shared renderer used for text-like scalar fields. This means email addresses inherit the standard truncation, search, and text-cell behavior rather than using a dedicated email-specific list widget.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget through the shared text-column path |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "email",
"isSearchable": true
}
}No additional core list or tree widgets are currently registered specifically for email.
password
Core list and tree rendering should not be assumed for password.
The core list-column dispatch does not currently include a dedicated password renderer, and password fields are generally not intended for ordinary list presentation.
| Concern | How it works |
|---|---|
| Default widget | No dedicated core list widget is currently wired for password |
| Widget selection | No core password list-column path is currently registered in list dispatch |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | None specific to password at the core list level |
Show example from metadata
{
"type": "field",
"attrs": {
"name": "password"
}
}No additional core list or tree widgets are currently registered specifically for password.
selectionStatic
By default, selectionStatic renders as a text-style column whose visible value is derived from the authored option labels.
The list path uses DefaultTextListWidget through the shared text-column renderer. For single-select values, that shared renderer maps stored values to their human-readable labels using selectionStaticValues, so list and tree views show the authored label rather than the raw stored token.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget through the shared selection-static column path |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "status",
"isSearchable": true
}
}No additional core list or tree widgets are currently registered specifically for selectionStatic.
selectionDynamic
By default, selectionDynamic renders as a text-style column whose visible value comes directly from the data returned for the row.
Unlike selectionStatic, the core list path does not perform provider lookups or label mapping at render time. The shared text widget simply displays the current row value as supplied by the dataset.
| Concern | How it works |
|---|---|
| Default widget | DefaultTextListWidget through the shared selection-dynamic column path |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant layout attrs | truncateAfter, enableGlobalSearch |
Show default example
{
"type": "field",
"attrs": {
"name": "couponVenueType"
}
}No additional core list or tree widgets are currently registered specifically for selectionDynamic.
many-to-one
In list and tree views, many-to-one renders as one related record.
By default, the column uses DefaultRelationManyToOneListWidget, which displays the related value as an interactive text cell. When the column is enabled, the cell can open the related record directly.
| Concern | How it works |
|---|---|
| Default list widget | DefaultRelationManyToOneListWidget |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable, disabled |
| Relevant widget-level attrs | coModelFieldToDisplay, disabled |
Show default example
{
"type": "field",
"attrs": {
"name": "institute",
"sortable": true,
"filterable": true,
"isSearchable": true
}
}| Widget | Use when | Extra attrs |
|---|---|---|
SolidManyToOneRelationAvatarListWidget | The related value should render as an avatar badge instead of as a text link | None |
SolidManyToOneRelationAvatarListWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget derives the avatar label from the related record and renders the relation as an avatar badge instead of the default text cell. |
{
"type": "field",
"attrs": {
"viewWidget": "SolidManyToOneRelationAvatarListWidget",
"label": "Client",
"name": "client",
"isSearchable": true,
"searchField": "client.nameOfBusiness",
"searchMatchMode": "$contains"
}
}one-to-many
In list and tree views, one-to-many renders as a compact summary of the related collection.
By default, the column uses DefaultRelationOneToManyListWidget, which shows the first related label and a +N indicator when additional related records are present.
| Concern | How it works |
|---|---|
| Default list widget | DefaultRelationOneToManyListWidget |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant widget-level attrs | coModelFieldToDisplay |
Show default example
{
"type": "field",
"attrs": {
"name": "questionPossibleAnswersMaster",
"label": "Possible Response",
"isSearchable": true
}
}No additional core list or tree widgets are currently registered specifically for one-to-many.
many-to-many
In list and tree views, many-to-many renders as a compact summary of the related membership set.
By default, the column uses DefaultRelationManyToManyListWidget, which shows the first related label and a +N indicator when additional related records are present.
| Concern | How it works |
|---|---|
| Default list widget | DefaultRelationManyToManyListWidget |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant widget-level attrs | coModelFieldToDisplay |
Show default example
{
"type": "field",
"attrs": {
"name": "domains",
"isSearchable": true,
"searchField": "domains.name",
"searchMatchMode": "$contains"
}
}| Widget | Use when | Extra attrs |
|---|---|---|
SolidManyToManyRelationAvatarListWidget | The relation membership should render as avatar badges instead of as plain text | None |
SolidManyToManyRelationAvatarListWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget derives avatar labels from the related records and renders the first related record as an avatar with a +N count for the remainder. |
{
"type": "field",
"attrs": {
"name": "respondentUser",
"viewWidget": "SolidManyToManyRelationAvatarListWidget",
"isSearchable": true
}
}mediaSingle
In list and tree views, mediaSingle renders as a thumbnail or file-style preview for the stored asset.
By default, the column uses DefaultMediaSingleListWidget, which shows an inline preview for image, audio, and video assets and falls back to a file-style preview for document-heavy uploads.
| Concern | How it works |
|---|---|
| Default list widget | DefaultMediaSingleListWidget |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant field attrs | mediaTypes, mediaMaxSizeKb, mediaStorageProviderUserKey |
Show default example
{
"type": "field",
"attrs": {
"name": "logo",
"sortable": true,
"filterable": true,
"isSearchable": true
}
}No additional core list or tree widgets are currently registered specifically for mediaSingle.
mediaMultiple
In list and tree views, mediaMultiple renders as a preview of the first asset plus a count for the remaining items.
By default, the column uses DefaultMediaMultipleListWidget, which shows the lead asset, adds a +N count when more files are present, and supports preview or download flows depending on the media type.
| Concern | How it works |
|---|---|
| Default list widget | DefaultMediaMultipleListWidget |
| Widget selection | Applied automatically when viewWidget is not provided |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant field attrs | mediaTypes, mediaMaxSizeKb, mediaStorageProviderUserKey |
Show default example
{
"type": "field",
"attrs": {
"name": "payByLinkInvoiceFile"
}
}No additional core list or tree widgets are currently registered specifically for mediaMultiple.
computed
In list and tree views, computed does not use a dedicated widget family of its own.
Instead, SolidX renders the field using the same column style that would normally be used for the computed value type, such as text, numeric, boolean, date, or datetime.
| Concern | How it works |
|---|---|
| Default list widget | No dedicated computed widget; rendering is delegated by computedFieldValueType |
| Widget selection | Text-like values use the text-column path; numeric values use the numeric-column path; date-like and boolean values use their matching scalar-column paths |
| Typical field-node attrs | name, label, sortable, filterable, isSearchable |
| Relevant field attrs | computedFieldValueType |
Show default example
{
"type": "field",
"attrs": {
"name": "transactionId"
}
}No additional core list or tree widgets are currently registered specifically for computed.
Tree View
Use tree when the same dataset needs to be understood hierarchically.
Tree views reuse the same field-column widgets as list views. In practice, that means the shortText widget coverage documented in the list section above also applies to tree unless a tree-specific renderer is introduced later.
Form View
Use form when users are creating, editing, or inspecting a single record.
Common form Layout Attrs
| Attr | Purpose |
|---|---|
name | Internal layout identifier |
label | Optional layout-level label |
className | Grid and spacing classes applied to layout containers |
workflowField | Workflow control field for workflow-driven forms |
workflowFieldUpdateEnabled | Controls whether the workflow field can change |
disabled | Disables the entire form layout |
readonly | Makes the entire form layout read-only |
showAddFormButton | Shows or hides the create button |
showEditFormButton | Shows or hides the edit button |
showDeleteFormButton | Shows or hides the delete button |
formButtons | Adds custom form-level actions |
Common form Field-Node Attrs
These attrs belong to the layout node, not to the field metadata entity:
| Attr | Purpose |
|---|---|
name | Chooses the field to render |
label | Overrides the default field label |
description | Overrides the default help text |
className | Controls field placement in the grid |
showLabel | Hides or shows the rendered field label |
disabled | Disables the field at the layout level |
readonly | Makes the field read-only at the layout level |
editWidget | Selects the widget used in edit mode |
viewWidget | Selects the widget used in view mode |
autoComplete | Controls the browser autocomplete behavior for text-style edit widgets that support it |
shortText
By default, shortText renders as a standard single-line text input in edit mode and as plain text in view mode.
The default edit renderer is DefaultShortTextFormEditWidget, and the default view renderer is DefaultShortTextFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultShortTextFormEditWidget |
| Default view widget | DefaultShortTextFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, className, showLabel, disabled, readonly |
| Relevant widget-level attrs | autoComplete on the default edit widget |
Show default example
{
"type": "field",
"attrs": {
"name": "bankUserId"
}
}| Widget | Use when | Extra attrs |
|---|---|---|
MaskedShortTextFormEditWidget | The value should be edited like a password-style secret input | autoComplete |
MaskedShortTextFormViewWidget | The value should stay masked in view mode | None |
SolidShortTextFieldAvatarWidget | The value should be displayed in view mode with an avatar-style badge | None |
MaskedShortTextFormEditWidget
| Attr | Purpose |
|---|---|
autoComplete | Passed through to the underlying password-style input so browser autocomplete can be enabled, disabled, or tuned for secret-like values. |
{
"type": "field",
"attrs": {
"name": "paymentGatewayAccessSecret",
"viewWidget": "maskedShortTextForm",
"editWidget": "maskedShortTextEdit"
}
}MaskedShortTextFormViewWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget renders the same field label as the default view widget, but masks the visible value instead of showing it directly. |
{
"type": "field",
"attrs": {
"name": "paymentGatewayAccessSecret",
"viewWidget": "maskedShortTextForm",
"editWidget": "maskedShortTextEdit"
}
}SolidShortTextFieldAvatarWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget derives avatar initials directly from the field value and renders the value with an avatar-style badge in view mode. |
{
"type": "field",
"attrs": {
"label": "Vendor Name",
"name": "name",
"viewWidget": "SolidShortTextFieldAvatarWidget"
}
}longText
By default, longText renders as a multiline text area in edit mode and as a default read-only text display in view mode.
The default edit renderer is DefaultLongTextFormEditWidget.
longText supports richer form-side rendering than list-side rendering. Depending on the chosen widget, it can be edited as plain multiline text, as a structured JSON editor, or as a code editor.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultLongTextFormEditWidget |
| Default view widget | Plain read-only text display |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, className, showLabel, disabled, readonly |
| Relevant widget-level attrs | None beyond the standard field-node attrs |
Show default example
{
"type": "field",
"attrs": {
"name": "description"
}
}| Widget | Use when | Extra attrs |
|---|---|---|
DynamicJsonEditorFormEditWidget | The value is stored as text but authored as a structured JSON array or object | jsonSchema, jsonSchemaShowPreview |
DynamicJsonEditorFormViewWidget | The value is stored as text but should render as structured read-only JSON content | jsonSchema |
CodeEditorFormEditWidget | The value should be edited in a code-oriented editor rather than a plain textarea | editorLanguage, height, fontSize |
DynamicJsonEditorFormEditWidget
| Attr | Purpose |
|---|---|
jsonSchema | Required. Defines the structure that the widget uses to render each JSON entry and choose the correct embedded input controls. |
jsonSchemaShowPreview | Optional. When true, shows a live JSON preview beneath the structured editor. |
{
"type": "field",
"attrs": {
"name": "templateFields",
"editWidget": "jsonEditor",
"viewWidget": "jsonViewer",
"jsonSchemaShowPreview": false,
"jsonSchema": {
"name": {
"required": true,
"type": "string"
},
"displayName": {
"required": true,
"type": "string"
},
"datatype": {
"required": true,
"type": "selectionStatic",
"allowedValues": [
"string",
"numeric",
"datetime"
]
},
"description": {
"required": true,
"type": "longText"
}
}
}
}DynamicJsonEditorFormViewWidget
| Attr | Purpose |
|---|---|
jsonSchema | Required. Defines how the widget should interpret and render the stored JSON value in structured read-only mode. |
{
"type": "field",
"attrs": {
"name": "templateFields",
"editWidget": "jsonEditor",
"viewWidget": "jsonViewer",
"jsonSchemaShowPreview": false,
"jsonSchema": {
"name": {
"required": true,
"type": "string"
},
"displayName": {
"required": true,
"type": "string"
},
"datatype": {
"required": true,
"type": "selectionStatic",
"allowedValues": [
"string",
"numeric",
"datetime"
]
},
"description": {
"required": true,
"type": "longText"
}
}
}
}CodeEditorFormEditWidget
| Attr | Purpose |
|---|---|
editorLanguage | Sets the language mode used by the code editor, such as json or ts. |
height | Controls the visible editor height. Defaults to 200px. |
fontSize | Controls the editor font size. Defaults to 14px. |
{
"type": "field",
"attrs": {
"name": "defaultValue",
"editWidget": "codeEditor",
"editorLanguage": "json"
}
}richText
By default, richText renders as a rich-text editor in edit mode and as formatted HTML content in view mode.
The supported richText widget surface is intentionally simple at the moment: one default edit widget and one default view widget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultRichTextFormEditWidget |
| Default view widget | DefaultRichTextFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, className, showLabel, disabled, readonly |
| Relevant widget-level attrs | None beyond the standard field-node attrs |
Show default example
{
"type": "field",
"attrs": {
"name": "content",
"label": "content"
}
}No additional form widgets are currently registered specifically for richText.
json
By default, json renders in forms using a JSON-oriented code editor in both edit and view modes.
The supported core json widget surface is intentionally simple at the moment: one default edit widget and one default view widget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultJsonFormEditWidget |
| Default view widget | DefaultJsonFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, className, showLabel, readonly |
| Relevant widget-level attrs | height, fontSize |
Show default example
{
"type": "field",
"attrs": {
"name": "metadata"
}
}No additional core form widgets are currently registered specifically for json.
int
By default, int renders as a number input in edit mode and as a plain numeric read-only value in view mode.
The default edit path uses DefaultIntegerFormEditWidget. The default view path uses DefaultIntegerFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultIntegerFormEditWidget |
| Default view widget | DefaultIntegerFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly, autoComplete |
| Relevant field attrs | min, max, required |
Show default example
{
"type": "field",
"attrs": {
"name": "pageCount"
}
}| Widget | Use when | Extra attrs |
|---|---|---|
SolidIntegerSliderStyleFormEditWidget | The integer should be chosen from a bounded range instead of typed manually | No widget-specific attrs; the widget relies on the field metadata min and max values |
SolidIntegerSliderStyleFormEditWidget
| Attr | Purpose |
|---|---|
| No widget-specific attrs | This widget renders the field as a slider and relies on the field metadata min and max values to determine the selectable range. When bounds are not authored, the current implementation falls back to its own default range. |
{
"type": "field",
"attrs": {
"name": "weightage",
"editWidget": "integerSlider"
}
}No additional core form view widgets are currently registered specifically for int.
bigint
By default, bigint reuses the same whole-number editing experience as int.
The form field factory currently routes bigint through SolidIntegerField, so the default edit path uses DefaultIntegerFormEditWidget and the default view path uses DefaultIntegerFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultIntegerFormEditWidget |
| Default view widget | DefaultIntegerFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly, autoComplete |
| Relevant field attrs | required |
Show default example
{
"type": "field",
"attrs": {
"name": "id"
}
}No additional core form widgets are currently registered specifically for bigint.
decimal
By default, decimal renders as a number input in edit mode and as a plain numeric read-only value in view mode.
The default edit path uses DefaultDecimalFormEditWidget. The default view path uses DefaultDecimalFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultDecimalFormEditWidget |
| Default view widget | DefaultDecimalFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly |
| Relevant field attrs | min, max, required |
Show default example
{
"type": "field",
"attrs": {
"name": "latePaymentFees"
}
}No additional core form widgets are currently registered specifically for decimal.
boolean
By default, boolean renders as a checkbox-style control in edit mode and as a readable true-or-false value in view mode.
The default edit path resolves through the booleanCheckbox alias to SolidBooleanCheckboxStyleFormEditWidget. The default view path uses DefaultBooleanFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | SolidBooleanCheckboxStyleFormEditWidget via the booleanCheckbox alias |
| Default view widget | DefaultBooleanFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly, checkboxLabel, trueLabel, falseLabel |
| Relevant layout attrs | disabled, readonly |
Show default example
{
"type": "field",
"attrs": {
"name": "isPBLEnabled",
"checkboxLabel": "IS PBL Enabled"
}
}| Widget | Use when | Extra attrs |
|---|---|---|
DefaultBooleanFormEditWidget | The field should render as an explicit yes-or-no segmented choice instead of as a checkbox | trueLabel, falseLabel |
SolidBooleanSwitchStyleFormEditWidget | The field should render as a switch-style control | None |
DefaultBooleanFormEditWidget
| Attr | Purpose |
|---|---|
trueLabel | Replaces the default True label in the segmented control with a business-friendly positive-state label. |
falseLabel | Replaces the default False label in the segmented control with a business-friendly negative-state label. |
{
"type": "field",
"attrs": {
"name": "discrepencyClear",
"editWidget": "booleanSelectbox",
"trueLabel": "Yes",
"falseLabel": "No"
}
}SolidBooleanSwitchStyleFormEditWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget focuses on the control style rather than on additional configuration. It renders the field as a switch while still respecting the standard boolean field-node attrs such as label, disabled, and readonly. |
{
"type": "field",
"attrs": {
"name": "accessToSystem",
"editWidget": "SolidBooleanSwitchStyleFormEditWidget"
}
}No additional core form view widgets are currently registered specifically for boolean.
date
By default, date renders as a date picker in edit mode and as a formatted date in view mode.
The default edit path uses DefaultDateFormEditWidget. The default view path uses DefaultDateFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultDateFormEditWidget |
| Default view widget | DefaultDateFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly, placeholder, format |
| Relevant layout attrs | disabled, readonly |
Show default example
{
"type": "field",
"attrs": {
"name": "dateOfApproval",
"label": "Date of Approval",
"format": "DD/MM/YYYY HH:mm:ss",
"className": "col-6"
}
}No additional core form widgets are currently registered specifically for date.
datetime
By default, datetime renders as a date-time picker in edit mode and as a formatted date-time value in view mode.
The default edit path uses DefaultDateTimeFormEditWidget. The default view path uses DefaultDateTimeFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultDateTimeFormEditWidget |
| Default view widget | DefaultDateTimeFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly, placeholder, format |
| Relevant layout attrs | disabled, readonly |
Show default example
{
"type": "field",
"attrs": {
"name": "startTime",
"format": "DD/MM/YYYY HH:mm:ss"
}
}No additional core form widgets are currently registered specifically for datetime.
time
By default, time renders as a time-only picker in edit mode and as a formatted time value in view mode.
The default edit path uses DefaultTimeFormEditWidget. The default view path uses DefaultTimeFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultTimeFormEditWidget |
| Default view widget | DefaultTimeFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly, format |
| Relevant layout attrs | disabled, readonly |
Show default example
{
"type": "field",
"attrs": {
"name": "startTime"
}
}No additional core form widgets are currently registered specifically for time.
email
By default, email renders as an email-oriented input in edit mode and as plain read-only text in view mode.
The default edit path uses DefaultEmailFormEditWidget. The default read-only display follows the shared plain-text view path used for text-like scalar fields.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultEmailFormEditWidget |
| Default view behavior | Plain read-only text display |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly, autoComplete |
| Relevant field attrs | max, regexPattern, regexPatternNotMatchingErrorMsg |
Show default example
{
"type": "field",
"attrs": {
"name": "payByLinkCustomerEmailAddress"
}
}No additional core form widgets are currently registered specifically for email.
password
By default, password uses two different edit experiences depending on context and a masked read-only view.
When a record is being created, the default create path uses DefaultPasswordFormCreateWidget, which includes password and confirm-password inputs. When an existing record is being edited, the default edit path uses DefaultPasswordFormEditWidget, which opens a controlled change-password dialog. The default view path uses DefaultPasswordFormViewWidget.
| Concern | How it works |
|---|---|
| Default create widget | DefaultPasswordFormCreateWidget |
| Default edit widget | DefaultPasswordFormEditWidget |
| Default view widget | DefaultPasswordFormViewWidget |
| Widget selection | Applied automatically when createWidget, editWidget, or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly, createWidget, editWidget, viewWidget |
| Relevant field attrs | min, max, regexPattern, regexPatternNotMatchingErrorMsg |
Show default example
{
"type": "field",
"attrs": {
"name": "password"
}
}No additional core form widgets are currently registered specifically for password.
selectionStatic
By default, selectionStatic renders as an autocomplete-driven picker in edit mode and as the authored label in view mode.
The default edit path uses DefaultSelectionStaticAutocompleteFormEditWidget. The default view path uses DefaultSelectionStaticFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultSelectionStaticAutocompleteFormEditWidget |
| Default view widget | DefaultSelectionStaticFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, showLabel, disabled, readonly, multiSelect |
| Relevant field attrs | selectionStaticValues, selectionValueType, isMultiSelect |
Show default example
{
"type": "field",
"attrs": {
"name": "type"
}
}| Widget | Use when | Extra attrs |
|---|---|---|
SolidSelectionStaticRadioFormEditWidget | The option set is small and should be shown as explicit radio choices | None |
SolidSelectionStaticSelectButtonFormEditWidget | The field should render as segmented selection buttons instead of an autocomplete | None |
SolidSelectionStaticRadioFormEditWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget focuses on presentation rather than on additional configuration. It renders the authored options as radio choices and relies on the standard field-node attrs such as label, disabled, and readonly. |
Important note: this widget supports single-select only. When multiSelect or isMultiSelect is enabled, the widget reports that the render mode is not supported.
Example coming soon..
SolidSelectionStaticSelectButtonFormEditWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget renders the authored options as segmented selection buttons and relies on the standard field-node attrs such as label, disabled, and readonly. |
Important note: this widget supports single-select only. When multiSelect or isMultiSelect is enabled, the widget reports that the render mode is not supported.
{
"type": "field",
"attrs": {
"name": "enableSeo",
"editWidget": "SolidSelectionStaticSelectButtonFormEditWidget"
}
}No additional core form view widgets are currently registered specifically for selectionStatic.
selectionDynamic
By default, selectionDynamic renders as a provider-backed autocomplete in edit mode and as the current selected label or value in view mode.
The default edit path uses DefaultSelectionDynamicFormEditWidget. The default view path uses DefaultSelectionDynamicFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultSelectionDynamicFormEditWidget |
| Default view widget | DefaultSelectionDynamicFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, showLabel, disabled, readonly, multiSelect, whereClause |
| Relevant field attrs | selectionDynamicProvider, selectionDynamicProviderCtxt, selectionValueType, isMultiSelect |
Show default example
{
"type": "field",
"attrs": {
"name": "couponVenueType"
}
}No additional core form widgets are currently registered specifically for selectionDynamic.
many-to-one
By default, many-to-one renders as an autocomplete-based relation picker in edit mode and as a simple related-value display in view mode.
The default edit path uses DefaultRelationManyToOneFormEditWidget. The default view path uses DefaultRelationManyToOneFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultRelationManyToOneFormEditWidget |
| Default view widget | DefaultRelationManyToOneFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, showLabel, disabled, readonly |
| Relevant widget-level attrs | coModelFieldToDisplay, autocompleteMatchMode, whereClause, inlineCreate, inlineCreateLayout, inlineCreateAutoSave |
Show default example
{
"type": "field",
"attrs": {
"name": "institute"
}
}| Widget | Use when | Extra attrs |
|---|---|---|
PseudoRelationManyToOneFormWidget | The field should behave like a custom cross-model lookup rather than a standard metadata relation | parentModelName, childModelName, parentFieldName, childFieldName, parentModuleName, childModuleName, parentFieldLabels, parentSearchFields, whereClause, inlineCreateLayout, inlineCreateAutoSave |
SolidRelationFieldAvatarFormWidget | The related value should render as avatar badges in view mode | None |
PseudoRelationManyToOneFormWidget
| Attr | Purpose |
|---|---|
parentModelName | Identifies the lookup model used to drive the pseudo relation search experience. |
childModelName | Identifies the model being edited from the current form context. |
parentFieldName | The field on the lookup model that supplies the stored or displayed value. |
childFieldName | The target field on the child model that should receive the selected value. |
parentModuleName | Module name for the lookup model. |
childModuleName | Module name for the child model. |
parentFieldLabels | Optional list of lookup-model fields to combine into the visible autocomplete label. |
parentSearchFields | Optional list of lookup-model fields to search across instead of only the default parent field. |
whereClause | Optional JSON-templated filter applied while resolving autocomplete candidates. |
inlineCreateLayout | Optional embedded create layout used by the inline create dialog. |
inlineCreateAutoSave | Controls whether the inline create dialog auto-saves the created relation record. |
{
"type": "field",
"attrs": {
"name": "assignedTo",
"label": "Assigned To",
"viewWidget": "PseudoRelationManyToOneFormWidget",
"editWidget": "PseudoRelationManyToOneFormWidget",
"parentModelName": "employee",
"childModelName": "frmsLead",
"parentFieldName": "employeeName",
"childFieldName": "assignedTo",
"childModuleName": "frms",
"parentModuleName": "peoples",
"parentFieldLabels": [
"employeeId",
"employeeName"
]
}
}SolidRelationFieldAvatarFormWidget
| Attr | Purpose |
|---|---|
| No extra attrs | This widget renders the related value as an avatar-style badge in view mode. |
{
"type": "field",
"attrs": {
"label": "Organisation",
"name": "client",
"viewWidget": "SolidRelationFieldAvatarFormWidget"
}
}one-to-many
By default, one-to-many renders as an embedded child-collection manager in both edit and view modes.
The default edit path uses DefaultRelationOneToManyFormEditWidget. The default view path uses DefaultRelationOneToManyFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultRelationOneToManyFormEditWidget |
| Default view widget | DefaultRelationOneToManyFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, showLabel, disabled, readonly |
| Relevant widget-level attrs | whereClause, inlineListLayout, inlineCreateLayout, inlineCreateAutoSave |
Show default example
{
"type": "field",
"attrs": {
"name": "questionPossibleAnswersMaster"
}
}| Widget | Use when | Extra attrs |
|---|---|---|
PseudoRelationOneToManyFormWidget | The collection should be managed through a custom embedded list/create flow rather than through a standard metadata relation field | parentModelName, childModelName, parentFieldName, childFieldName, parentModuleName, childModuleName, whereClause, inlineListLayout, inlineCreateLayout, inlineCreateAutoSave |
PseudoRelationOneToManyFormWidget
| Attr | Purpose |
|---|---|
parentModelName | Identifies the parent model whose form is hosting the pseudo collection. |
childModelName | Identifies the related child model rendered in the embedded list. |
parentFieldName | Parent-side field whose value should be used to scope the child collection. |
childFieldName | Child-side field used to bind each child record back to the parent record. |
parentModuleName | Module name for the parent model. |
childModuleName | Module name for the child model. |
whereClause | Optional JSON filter merged into the embedded child list query. |
inlineListLayout | Optional custom list layout used when rendering the embedded child collection. |
inlineCreateLayout | Optional custom form layout used when creating or editing child records inline. |
inlineCreateAutoSave | Controls whether inline child forms save automatically. |
{
"type": "custom",
"attrs": {
"name": "employeeDetails",
"widget": "PseudoRelationOneToManyFormWidget",
"parentModelName": "employee",
"childModelName": "employeeDetail",
"parentFieldName": "employeeId",
"childFieldName": "employeeId",
"inlineCreate": "false",
"childModuleName": "peoples",
"parentModuleName": "peoples"
}
}many-to-many
By default, many-to-many renders as a relation picker in edit mode and as a list-style relation browser in view mode.
The default edit path uses DefaultRelationManyToManyAutoCompleteFormEditWidget. The default view path uses DefaultRelationManyToManyListFormEditWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultRelationManyToManyAutoCompleteFormEditWidget |
| Default view widget | DefaultRelationManyToManyListFormEditWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, showLabel, disabled, readonly |
| Relevant widget-level attrs | autocompleteMatchMode, whereClause, inlineCreate, inlineListLayout, inlineCreateLayout, inlineCreateAutoSave |
Show default example
{
"type": "field",
"attrs": {
"name": "domains"
}
}| Widget | Use when | Extra attrs |
|---|---|---|
DefaultRelationManyToManyCheckBoxFormEditWidget | The full option set is small enough to render comfortably as a checkbox matrix | whereClause, inlineCreate |
DefaultRelationManyToManyListFormEditWidget | The relation should be managed through an embedded list with explicit link, unlink, create, and edit actions | whereClause, inlineListLayout, inlineCreateLayout, inlineCreateAutoSave |
DefaultRelationManyToManyCheckBoxFormEditWidget
| Attr | Purpose |
|---|---|
whereClause | Optional JSON-templated filter used when loading the full checkbox option set. |
inlineCreate | When enabled, shows the inline create affordance for creating new related records directly from the widget. |
{
"type": "field",
"attrs": {
"name": "users",
"editWidget": "DefaultRelationManyToManyCheckBoxFormEditWidget",
"showLabel": false
}
}DefaultRelationManyToManyListFormEditWidget
| Attr | Purpose |
|---|---|
whereClause | Optional JSON filter merged into the embedded relation list query. |
inlineListLayout | Optional custom list layout used for the embedded relation list. |
inlineCreateLayout | Optional custom form layout used when creating or editing related records inline. |
inlineCreateAutoSave | Controls whether inline create/edit dialogs auto-save related records. |
Example coming soon..
mediaSingle
By default, mediaSingle renders as a single-file upload and preview experience in edit mode and as a preview or download surface in view mode.
The default edit path uses DefaultMediaSingleFormEditWidget. The default view path uses DefaultMediaSingleFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultMediaSingleFormEditWidget |
| Default view widget | DefaultMediaSingleFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, showLabel, disabled, readonly |
| Relevant field attrs | mediaTypes, mediaMaxSizeKb, mediaStorageProviderUserKey |
Show default example
{
"type": "field",
"attrs": {
"name": "logo"
}
}No additional core form widgets are currently registered specifically for mediaSingle.
mediaMultiple
By default, mediaMultiple renders as a multi-file upload and preview experience in edit mode and as a gallery-style preview or download surface in view mode.
The default edit path uses DefaultMediaMultipleFormEditWidget. The default view path uses DefaultMediaMultipleFormViewWidget.
| Concern | How it works |
|---|---|
| Default edit widget | DefaultMediaMultipleFormEditWidget |
| Default view widget | DefaultMediaMultipleFormViewWidget |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, showLabel, disabled, readonly |
| Relevant field attrs | mediaTypes, mediaMaxSizeKb, mediaStorageProviderUserKey |
Show default example
{
"type": "field",
"attrs": {
"name": "payByLinkInvoiceFile"
}
}No additional core form widgets are currently registered specifically for mediaMultiple.
computed
By default, computed renders as a read-only field and is hidden while a new record is still being created.
The default experience uses the built-in computed-field renderer for both edit and view modes.
| Concern | How it works |
|---|---|
| Default edit widget | Built-in computed renderer |
| Default view widget | Built-in computed renderer |
| Widget selection | Applied automatically when editWidget or viewWidget is not provided |
| Typical field-node attrs | name, label, description, showLabel, readonly |
| Relevant field attrs | computedFieldValueType, computedFieldValueProvider, computedFieldTriggerConfig |
Show default example
{
"type": "field",
"attrs": {
"name": "transactionId",
"readonly": true
}
}No additional core form widgets are currently registered specifically for computed.
Best Practices
Layout Organization
- Use logical grouping: Group related fields in columns with descriptive labels
- Progressive disclosure: Use tabs (notebooks) for complex forms
- Responsive design: Use appropriate grid classes for different screen sizes
- Field ordering: Place important fields first, follow logical workflow
Security Considerations
- Role-based access: Configure view actions based on user roles
- Field-level security: Hide sensitive fields from unauthorized users
- Audit trails: Enable audit tracking for sensitive operations
Performance Optimization
- Pagination: Always enable pagination for large datasets
- Field selection: Only display necessary fields in list views
Card And Kanban Views
card and kanban views are usually documented at the card-composition level rather than at the individual field-widget level.
They remain important view families, but they do not currently need the same per-field widget catalog that list, tree, and form need.