Components Reference
This page documents every UI component type available in Aptean Mesh page definitions. All property names and defaults are taken directly from the Kotlin source in MeshClient.
Components are placed in the body array of a page. Each component is a flat JSON object with a type discriminator that identifies the component kind:
{
"body": [
{ "type": "text", "text": "Hello" },
{ "type": "button", "action": "doSomething", "label": "Click Me" }
]
}
Base Component Interface
All components implement the Component sealed interface (defined in Component.kt). Every component inherits these optional properties:
| Property | Type | Default | Description |
|---|---|---|---|
visibleKey | string? | null | Data binding key that controls visibility. The component is hidden when the bound value is false or null. |
enabledKey | string? | null | Data binding key that controls interactivity. The component is disabled (dimmed, non-interactive) when the bound value is false. |
These properties are not repeated in every component table below but are available on all components.
Text
Type discriminator: "text"
Displays static or data-bound text content. Defined in TextModel.kt as TextComponent.
| Property | Type | Default | Description |
|---|---|---|---|
text | string? | null | Static text content. Ignored if valueKey is provided and resolves to a value. |
valueKey | string? | null | Data binding key to dynamically resolve the displayed text from page data. |
tone | Tone | MEDIUM | Visual tone affecting text color. |
style | TextStyle | BODY | Typography style variant. |
TextStyle enum: HEADER, BODY, CAPTION
Either text or valueKey should be provided. When both are set, valueKey takes precedence if the bound data is available.
Examples
// Static header
{ "type": "text", "text": "Sales Orders", "style": "HEADER" }
// Data-bound value
{ "type": "text", "valueKey": "item.Description" }
// Caption with tone
{ "type": "text", "text": "Warehouse", "style": "CAPTION", "tone": "HIGH" }
Button
Type discriminator: "button"
A tappable button that executes an action. Defined in ButtonModel.kt as ButtonComponent.
| Property | Type | Default | Description |
|---|---|---|---|
action | string | required | Action identifier to execute when the button is tapped. Must reference an action in contract.actions. |
label | string | required | Text label displayed on the button. |
variant | ButtonVariant | FILLED | Visual style variant. |
tone | Tone | HIGH | Visual emphasis level affecting button color. |
ButtonVariant enum: FILLED, TONAL, OUTLINED
- FILLED -- Solid background with the tone color. Highest visual weight.
- TONAL -- Subtle tinted background. Medium visual weight.
- OUTLINED -- Border-only with transparent background. Lowest visual weight.
Examples
// Default filled button
{ "type": "button", "action": "loadData", "label": "Load" }
// Tonal variant
{ "type": "button", "action": "viewCustomers", "label": "Customer List", "variant": "TONAL" }
// Outlined, conditionally visible
{
"type": "button",
"action": "undoAction",
"label": "Undo",
"variant": "OUTLINED",
"tone": "WARNING",
"visibleKey": "canUndo"
}
Input
Type discriminator: "input"
A text input field for capturing user entry. Defined in InputModel.kt as InputComponent.
| Property | Type | Default | Description |
|---|---|---|---|
inputId | string | required | Unique identifier for this input. The entered value is available in action params and via State.GetInput() in AL. |
label | string? | null | Label text displayed above the input field. |
placeholder | string? | null | Placeholder text shown when the input is empty. |
required | boolean | false | Whether the input must have a value before form submission. |
action | string? | null | Action identifier to execute when the user presses Enter or submits the input. |
Examples
// Search input that triggers an action on Enter
{
"type": "input",
"inputId": "query",
"placeholder": "Search documents...",
"action": "onLoadDocuments"
}
// Required input with label
{
"type": "input",
"inputId": "lotNumber",
"label": "Lot Number",
"placeholder": "Enter or scan lot...",
"required": true,
"action": "onValidateLot"
}
// Conditionally visible input
{
"type": "input",
"inputId": "searchQuery",
"placeholder": "Scan or search Item/Reference...",
"action": "onLoadLines",
"visibleKey": "showSearch"
}
Card
Type discriminator: "card"
A Material Design card displaying structured label-value fields with optional actions. Defined in CardModel.kt as CardComponent.
| Property | Type | Default | Description |
|---|---|---|---|
title | string? | null | Title text displayed at the top of the card. |
fields | CardField[] | required | Array of label-value field pairs displayed in the card body. |
onTapAction | string? | null | Action identifier to execute when the card is tapped. |
contextActions | ActionItem[]? | null | Actions shown in the card's context menu (long-press or overflow). |
tone | Tone | MEDIUM | Visual tone affecting the card's background and text colors. |
CardField
A label-value pair within a card. Defined in CardModel.kt.
| Property | Type | Default | Description |
|---|---|---|---|
label | string | required | Display label for the field. |
valueKey | string | required | Data binding key to resolve the field value from page data. |
value | string? | null | Static fallback value used when valueKey does not resolve. |
tone | Tone | MEDIUM | Visual tone for this specific field's value text. |
Examples
// Simple card with fields
{
"type": "card",
"title": "Order Info",
"fields": [
{ "label": "Order No.", "valueKey": "order.No", "tone": "HIGH" },
{ "label": "Customer", "valueKey": "order.CustomerName" },
{ "label": "Status", "valueKey": "order.Status" }
]
}
// Tappable card with context actions
{
"type": "card",
"fields": [
{ "label": "Item", "valueKey": "item.Description" },
{ "label": "Quantity", "valueKey": "item.Qty", "tone": "HIGH" }
],
"onTapAction": "onItemTap",
"contextActions": [
{ "label": "Delete", "action": "deleteItem", "icon": "delete", "tone": "ERROR" }
]
}
Repeater
Type discriminator: "repeater"
Renders a scrollable list of items from bound data using a configurable template. Defined in RepeaterModel.kt as RepeaterComponent.
key property must be unique and stableThe key field is used by the client to diff the list and route tap events to the correct row.
- Uniqueness: If two rows in the same repeater share the same key value, the client cannot distinguish them — taps will route to the wrong item and the app will crash.
- Stability: A row's key must not change between API calls. If the key changes on a refresh (e.g., because you used a sequential index instead of a real business identifier), the client treats the old row as deleted and the new one as inserted, breaking selection state.
Always use a natural business key — Item."No.", WhseReceiptLine."Line No.", BinContent."Entry No." — never a loop counter or a generated value that changes per call. If no single field is unique, concatenate fields: ItemNo + '|' + VariantCode.
| Property | Type | Default | Description |
|---|---|---|---|
dataKey | string | required | Data binding key pointing to the array of items to render. |
key | string | required | Field name in each item used as a unique, stable identifier. Duplicate or changing keys crash the app — always use a natural business key. |
template | Template | CARD | Template type used to render each item. |
orientation | Orientation | V | Scroll direction. |
onTapAction | string? | null | Action identifier to execute when an item is tapped. The item's data is passed as context. |
trailingAction | TrailingAction? | null | Swipe-to-reveal action on the trailing edge of each item. |
emptyMessage | string? | null | Message displayed when the data array is empty. |
fields | RepeaterField[]? | null | Explicit field definitions to display. When null, all fields from the data are shown. |
hideKeys | boolean | false | When true, hides the key/label column in CARD template, showing only values. |
Template enum: CARD, GRID
- CARD -- Each item renders as a card with label-value rows.
- GRID -- Items render in a compact grid layout.
Orientation enum: V, H
- V -- Vertical scrolling list (default).
- H -- Horizontal scrolling list.
RepeaterField
Defines a specific field to display in each repeater item. Defined in RepeaterModel.kt.
| Property | Type | Default | Description |
|---|---|---|---|
key | string | required | Data key to read the value from in each item. |
label | string | required | Display label shown to the user. |
TrailingAction
Configuration for a swipe-to-reveal action on repeater items. Defined in RepeaterModel.kt.
| Property | Type | Default | Description |
|---|---|---|---|
action | string | required | Action identifier to execute when triggered. |
icon | string | required | Material icon name displayed for the action. |
visibleKey | string? | null | Page-level data key that controls whether the trailing action is shown at all. |
enabledKey | string? | null | Per-item data key that controls whether the action is interactive for a specific item. |
tone | Tone | HIGH | Visual emphasis for the icon. Use ERROR for destructive actions. |
Examples
// Basic vertical repeater
{
"type": "repeater",
"dataKey": "documents",
"key": "Document No",
"template": "CARD",
"onTapAction": "onDocumentTap",
"emptyMessage": "No documents found",
"fields": [
{ "key": "Type", "label": "Type" },
{ "key": "Source No", "label": "Source No" },
{ "key": "Document No", "label": "Document No" },
{ "key": "Date", "label": "Assignment Date" }
]
}
// Repeater with swipe-to-reveal trailing action
{
"type": "repeater",
"dataKey": "lines",
"key": "lineNo",
"template": "CARD",
"onTapAction": "onLineTap",
"trailingAction": {
"action": "onPrintLot",
"icon": "print",
"visibleKey": "showPrintAction"
},
"emptyMessage": "No lines found"
}
// Horizontal grid repeater
{
"type": "repeater",
"dataKey": "recentItems",
"key": "id",
"template": "GRID",
"orientation": "H",
"onTapAction": "onItemTap"
}
InfoPane
Type discriminator: "infoPane"
An information pane for displaying detailed item information, typically with an image, title/subtitle, header rows, and field list. Defined in InfoPaneModel.kt as InfoPaneComponent.
| Property | Type | Default | Description |
|---|---|---|---|
image | InfoPaneImage? | null | Image configuration for displaying an item image. |
title | string? | null | Static title text. Ignored if titleKey resolves. |
titleKey | string? | null | Data binding key to dynamically resolve the title. |
subtitle | string? | null | Static subtitle text. Ignored if subtitleKey resolves. |
subtitleKey | string? | null | Data binding key to dynamically resolve the subtitle. |
headerRowsKey | string? | null | Data binding key pointing to an array of header row objects (displayed below title/subtitle, often with a capacity gauge). |
fieldsKey | string? | null | Data binding key pointing to an array of label-value field objects. |
maxLines | int | 4 | Maximum number of detail lines to display before collapsing with a "show more" control. |
InfoPaneImage
Configuration for the image in an info pane. Defined in InfoPaneModel.kt.
| Property | Type | Default | Description |
|---|---|---|---|
urlKey | string? | null | Data binding key to resolve the image URL from page data. |
Example
{
"type": "infoPane",
"image": { "urlKey": "item.imageUrl" },
"titleKey": "item.description",
"subtitleKey": "item.itemNo",
"headerRowsKey": "item.capacityRows",
"fieldsKey": "item.details",
"maxLines": 6
}
SidePane
Type discriminator: "sidePane"
A sliding panel for master-detail navigation patterns. Displays a selectable list that updates the main content area when an item is chosen. Defined in SidePaneModel.kt as SidePaneComponent.
| Property | Type | Default | Description |
|---|---|---|---|
title | string? | null | Title displayed at the top of the side pane. |
dataKey | string | required | Data binding key pointing to the array of items to display. |
displayKey | string | required | Field name in each item used as the display label. |
loadAction | string | required | Action identifier to execute when loading the side pane data. |
selectAction | string | required | Action identifier to execute when an item is selected. |
slideFrom | string | "right" | Direction from which the pane slides in. |
The side pane is toggled via the footer. Set footer.enableSidePane to true to show the toggle button.
Example
{
"type": "sidePane",
"title": "Zones",
"dataKey": "zones",
"displayKey": "zoneName",
"loadAction": "loadZones",
"selectAction": "selectZone"
}
With the corresponding footer:
{
"footer": {
"enableSidePane": true
}
}
Selector
Type discriminator: "selector"
A selection control for choosing from predefined options. Renders as inline segmented chips or a dropdown modal. Defined in SelectorModel.kt as SelectorComponent.
| Property | JSON Key | Type | Default | Description |
|---|---|---|---|---|
type | selectorType | SelectorType | DROPDOWN | Visual variant. Note: serialized as selectorType in JSON due to @SerialName. |
prompt | prompt | string? | null | Prompt text displayed above the selector. |
valueKey | valueKey | string? | null | Data binding key to read/write the selected value. |
options | options | string[] | [] | Static list of option values. |
optionsKey | optionsKey | string? | null | Data binding key pointing to a dynamic array of options from the API response. |
onSelectAction | onSelectAction | string? | null | Action identifier to execute when the selection changes. |
SelectorType enum: SEGMENT, DROPDOWN
- SEGMENT -- Renders as inline segmented chips. Best for 2-4 options.
- DROPDOWN -- Renders as a dropdown/modal picker. Better for longer option lists.
Examples
// Segmented selector
{
"type": "selector",
"selectorType": "SEGMENT",
"valueKey": "selectedType",
"options": ["Inbound", "Internal", "Outbound"],
"onSelectAction": "onLoadDocuments"
}
// Dropdown selector with prompt
{
"type": "selector",
"selectorType": "DROPDOWN",
"prompt": "Select Warehouse",
"optionsKey": "warehouseList",
"valueKey": "selectedWarehouse",
"onSelectAction": "onWarehouseChanged"
}
ProgressBar
Type discriminator: "progressBar"
A progress indicator for visualizing completion status. Defined in ProgressBarModel.kt as ProgressBarComponent.
| Property | Type | Default | Description |
|---|---|---|---|
valueKey | string | required | Data binding key to resolve the current progress value. |
maxValueKey | string | required | Data binding key to resolve the maximum progress value. |
title | string? | null | Title displayed above the progress bar. |
showLabel | boolean | true | Whether to display the progress value as text. |
labelFormat | ProgressLabelFormat | FRACTION | Format for displaying the progress value. |
color | ProgressBarColor | SUCCESS | Color theme for the progress bar. |
ProgressLabelFormat enum: PERCENT, FRACTION
- PERCENT -- Displays as percentage (e.g., "75%").
- FRACTION -- Displays as fraction (e.g., "3/4").
ProgressBarColor enum: PRIMARY, SUCCESS, WARNING, ERROR
Example
{
"type": "progressBar",
"title": "Fulfillment",
"valueKey": "order.ShippedQty",
"maxValueKey": "order.TotalQty",
"showLabel": true,
"labelFormat": "FRACTION",
"color": "SUCCESS"
}
TreeMap
Type discriminator: "TREE_MAP"
Visualizes hierarchical data as nested rectangles where sizes are proportional to data values. Defined in TreeMapComponent.kt as TreeMapComponent.
| Property | Type | Default | Description |
|---|---|---|---|
dataKey | string | required | Data binding key pointing to the array of node data. |
idField | string | "id" | Field name containing the unique identifier for each node. |
parentField | string | "parentId" | Field name containing the parent node's identifier. Nodes with null parent are root nodes. |
valueField | string | "value" | Field name containing the numeric value that determines rectangle size. |
labelField | string | "label" | Field name containing the display label for each node. |
showLabels | boolean | true | Whether to display labels inside the rectangles. |
minNodeValue | float | 1.0 | Minimum value threshold -- nodes below this value are not displayed. |
Example
{
"type": "TREE_MAP",
"dataKey": "purchaseVolume",
"idField": "vendorId",
"parentField": "categoryId",
"valueField": "amount",
"labelField": "vendorName",
"showLabels": true,
"minNodeValue": 100
}
The data array should contain objects with the referenced fields:
{
"purchaseVolume": [
{ "vendorId": "1", "categoryId": null, "amount": 50000, "vendorName": "Category A" },
{ "vendorId": "2", "categoryId": "1", "amount": 30000, "vendorName": "Vendor X" },
{ "vendorId": "3", "categoryId": "1", "amount": 20000, "vendorName": "Vendor Y" }
]
}
StepActionInput
Type discriminator: "stepInput"
A step-based input component for guided data entry workflows (e.g., warehouse pick, put-away, or receipt scanning). Displays step metadata from bound data and triggers actions on submission or undo. Defined in StepActionInputComponent.kt.
| Property | Type | Default | Description |
|---|---|---|---|
dataKey | string | required | Data binding key pointing to the step metadata object. The step engine uses this to display prompts, expected values, and input fields. |
action | string | required | Action identifier to execute when the user submits the step (e.g., confirms a scan). |
onUndoAction | string? | null | Action identifier to execute when the user undoes the previous step. |
Example
{
"type": "stepInput",
"dataKey": "scanInput",
"action": "onScan",
"onUndoAction": "onUndo"
}
This component is typically used in workflow pages where a BC codeunit drives a step-by-step process. The dataKey resolves to a step metadata object that tells the client what to prompt for (e.g., "Scan Bin", "Enter Quantity").
Cross-Cutting Patterns
Visibility and Enabled Control
Every component supports visibleKey and enabledKey from the base Component interface:
{
"type": "button",
"action": "shipOrder",
"label": "Ship",
"visibleKey": "order.canShip",
"enabledKey": "order.isReady"
}
- When
visibleKeyresolves tofalseornull, the component is removed from the layout entirely. - When
enabledKeyresolves tofalse, the component is rendered but dimmed and non-interactive.
These keys are evaluated against the current page data, which updates whenever an API action returns new values.
Setting these from AL — write boolean values into the response using JsonHelper.WriteBoolean. The key name must exactly match the visibleKey/enabledKey string in the page JSON:
var
JsonHelper: Codeunit JsonHelper220FDW;
begin
// Controls whether the Post button is visible
JsonHelper.WriteBoolean(Result, 'canPost', HasRequiredData);
// Controls whether the Lot input appears
JsonHelper.WriteBoolean(Result, 'lotRequired', Item."Lot Nos." <> '');
// Controls whether the Submit button is interactive
JsonHelper.WriteBoolean(Result, 'isReady', (ItemNo <> '') and (Qty > 0));
end;
SetView for values that persist across callsIf the visibility flag must survive multiple RPC round-trips (e.g., set on Initialize, still respected on OnScan), use State.SetViewBool('canPost', true) instead of writing directly to Result. View state is round-tripped by the client on every call.
Complete example — Post button visible only when all fields are filled:
Page JSON:
{ "type": "button", "label": "#Post", "action": "onPost",
"tone": "SUCCESS", "visibleKey": "canPost" }
AL Initialize handler:
local procedure Initialize(var State: Codeunit MobileRPCState220FDW) Result: JsonObject
var
JsonHelper: Codeunit JsonHelper220FDW;
begin
// ... load data ...
JsonHelper.WriteBoolean(Result, 'canPost', DocumentNo <> '');
end;
Data Binding Conventions
Properties ending in Key are data binding references that resolve against the page's data store:
| Pattern | Description |
|---|---|
valueKey | Resolves a single display value |
dataKey | Resolves an array of items (repeaters, side panes, tree maps) |
titleKey / subtitleKey | Resolves dynamic title/subtitle text |
enabledKey / visibleKey | Resolves boolean visibility/enabled state |
optionsKey | Resolves a dynamic list of selector options |
urlKey | Resolves an image URL |
Values use dot-notation paths into the API response data:
| Expression | Resolves to |
|---|---|
order.No | The No field of the order object in the response |
item.Description | The Description field of the item object |
documents | The top-level documents array |
For repeater items, field keys reference properties within each array element directly (e.g., "key": "Document No" reads the Document No field from each item in the bound array).
Action References
Properties like action, onTapAction, onSelectAction, loadAction, and selectAction all reference entries in contract.actions by their id. The action type (api, navigate, etc.) determines what happens when the action is triggered.
Tone Usage Guidelines
| Tone | When to use |
|---|---|
HIGH | Primary actions, important values, key identifiers |
MEDIUM | Default for most content |
LOW | Secondary or de-emphasized information |
SUCCESS | Completed states, positive confirmations |
WARNING | Caution states, pending items |
ERROR | Destructive actions, error states, blocked items |
INFO | Informational highlights |
UnknownComponent (Fallback)
Type discriminator: "unknown"
When the mobile client receives a component type it does not recognise — for example because the server is running a newer version of the platform than the installed client app — it deserialises the component as an UnknownComponent instead of crashing.
In debug builds, the client renders a red error surface showing the unrecognised type name so developers can spot the version mismatch immediately. In release builds, the component is silently skipped and the rest of the page renders normally.
An UnknownComponent almost always means the client app is outdated relative to the server. Check the Min Client Version field on the App Bundle and ensure the device has updated to at least that version.
You do not author UnknownComponent intentionally — it is a safety net built into the client that prevents a schema version mismatch from crashing the app.
| INFO | Informational highlights |