Skip to main content

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:

PropertyTypeDefaultDescription
visibleKeystring?nullData binding key that controls visibility. The component is hidden when the bound value is false or null.
enabledKeystring?nullData 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.

PropertyTypeDefaultDescription
textstring?nullStatic text content. Ignored if valueKey is provided and resolves to a value.
valueKeystring?nullData binding key to dynamically resolve the displayed text from page data.
toneToneMEDIUMVisual tone affecting text color.
styleTextStyleBODYTypography style variant.

TextStyle enum: HEADER, BODY, CAPTION

note

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.

PropertyTypeDefaultDescription
actionstringrequiredAction identifier to execute when the button is tapped. Must reference an action in contract.actions.
labelstringrequiredText label displayed on the button.
variantButtonVariantFILLEDVisual style variant.
toneToneHIGHVisual 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.

PropertyTypeDefaultDescription
inputIdstringrequiredUnique identifier for this input. The entered value is available in action params and via State.GetInput() in AL.
labelstring?nullLabel text displayed above the input field.
placeholderstring?nullPlaceholder text shown when the input is empty.
requiredbooleanfalseWhether the input must have a value before form submission.
actionstring?nullAction 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.

PropertyTypeDefaultDescription
titlestring?nullTitle text displayed at the top of the card.
fieldsCardField[]requiredArray of label-value field pairs displayed in the card body.
onTapActionstring?nullAction identifier to execute when the card is tapped.
contextActionsActionItem[]?nullActions shown in the card's context menu (long-press or overflow).
toneToneMEDIUMVisual tone affecting the card's background and text colors.

CardField

A label-value pair within a card. Defined in CardModel.kt.

PropertyTypeDefaultDescription
labelstringrequiredDisplay label for the field.
valueKeystringrequiredData binding key to resolve the field value from page data.
valuestring?nullStatic fallback value used when valueKey does not resolve.
toneToneMEDIUMVisual 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.

IMPORTANT — The key property must be unique and stable

The 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 keyItem."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.

PropertyTypeDefaultDescription
dataKeystringrequiredData binding key pointing to the array of items to render.
keystringrequiredField name in each item used as a unique, stable identifier. Duplicate or changing keys crash the app — always use a natural business key.
templateTemplateCARDTemplate type used to render each item.
orientationOrientationVScroll direction.
onTapActionstring?nullAction identifier to execute when an item is tapped. The item's data is passed as context.
trailingActionTrailingAction?nullSwipe-to-reveal action on the trailing edge of each item.
emptyMessagestring?nullMessage displayed when the data array is empty.
fieldsRepeaterField[]?nullExplicit field definitions to display. When null, all fields from the data are shown.
hideKeysbooleanfalseWhen 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.

PropertyTypeDefaultDescription
keystringrequiredData key to read the value from in each item.
labelstringrequiredDisplay label shown to the user.

TrailingAction

Configuration for a swipe-to-reveal action on repeater items. Defined in RepeaterModel.kt.

PropertyTypeDefaultDescription
actionstringrequiredAction identifier to execute when triggered.
iconstringrequiredMaterial icon name displayed for the action.
visibleKeystring?nullPage-level data key that controls whether the trailing action is shown at all.
enabledKeystring?nullPer-item data key that controls whether the action is interactive for a specific item.
toneToneHIGHVisual 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.

PropertyTypeDefaultDescription
imageInfoPaneImage?nullImage configuration for displaying an item image.
titlestring?nullStatic title text. Ignored if titleKey resolves.
titleKeystring?nullData binding key to dynamically resolve the title.
subtitlestring?nullStatic subtitle text. Ignored if subtitleKey resolves.
subtitleKeystring?nullData binding key to dynamically resolve the subtitle.
headerRowsKeystring?nullData binding key pointing to an array of header row objects (displayed below title/subtitle, often with a capacity gauge).
fieldsKeystring?nullData binding key pointing to an array of label-value field objects.
maxLinesint4Maximum 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.

PropertyTypeDefaultDescription
urlKeystring?nullData 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.

PropertyTypeDefaultDescription
titlestring?nullTitle displayed at the top of the side pane.
dataKeystringrequiredData binding key pointing to the array of items to display.
displayKeystringrequiredField name in each item used as the display label.
loadActionstringrequiredAction identifier to execute when loading the side pane data.
selectActionstringrequiredAction identifier to execute when an item is selected.
slideFromstring"right"Direction from which the pane slides in.
note

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.

PropertyJSON KeyTypeDefaultDescription
typeselectorTypeSelectorTypeDROPDOWNVisual variant. Note: serialized as selectorType in JSON due to @SerialName.
promptpromptstring?nullPrompt text displayed above the selector.
valueKeyvalueKeystring?nullData binding key to read/write the selected value.
optionsoptionsstring[][]Static list of option values.
optionsKeyoptionsKeystring?nullData binding key pointing to a dynamic array of options from the API response.
onSelectActiononSelectActionstring?nullAction 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.

PropertyTypeDefaultDescription
valueKeystringrequiredData binding key to resolve the current progress value.
maxValueKeystringrequiredData binding key to resolve the maximum progress value.
titlestring?nullTitle displayed above the progress bar.
showLabelbooleantrueWhether to display the progress value as text.
labelFormatProgressLabelFormatFRACTIONFormat for displaying the progress value.
colorProgressBarColorSUCCESSColor 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.

PropertyTypeDefaultDescription
dataKeystringrequiredData binding key pointing to the array of node data.
idFieldstring"id"Field name containing the unique identifier for each node.
parentFieldstring"parentId"Field name containing the parent node's identifier. Nodes with null parent are root nodes.
valueFieldstring"value"Field name containing the numeric value that determines rectangle size.
labelFieldstring"label"Field name containing the display label for each node.
showLabelsbooleantrueWhether to display labels inside the rectangles.
minNodeValuefloat1.0Minimum 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.

PropertyTypeDefaultDescription
dataKeystringrequiredData binding key pointing to the step metadata object. The step engine uses this to display prompts, expected values, and input fields.
actionstringrequiredAction identifier to execute when the user submits the step (e.g., confirms a scan).
onUndoActionstring?nullAction 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 visibleKey resolves to false or null, the component is removed from the layout entirely.
  • When enabledKey resolves to false, 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;
Use SetView for values that persist across calls

If 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:

PatternDescription
valueKeyResolves a single display value
dataKeyResolves an array of items (repeaters, side panes, tree maps)
titleKey / subtitleKeyResolves dynamic title/subtitle text
enabledKey / visibleKeyResolves boolean visibility/enabled state
optionsKeyResolves a dynamic list of selector options
urlKeyResolves an image URL

Values use dot-notation paths into the API response data:

ExpressionResolves to
order.NoThe No field of the order object in the response
item.DescriptionThe Description field of the item object
documentsThe 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

ToneWhen to use
HIGHPrimary actions, important values, key identifiers
MEDIUMDefault for most content
LOWSecondary or de-emphasized information
SUCCESSCompleted states, positive confirmations
WARNINGCaution states, pending items
ERRORDestructive actions, error states, blocked items
INFOInformational 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.

When you see this in a debug build

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 |