Component Actions in CXTMS
Introductionβ
Component Actions define the behavior of components in CXTMS. They handle events, fetch data, and perform various operations within the application.
When to Use Actionsβ
Use actions when you need to:
- Respond to user interactions (e.g., button clicks)
- Update data in the application
- Navigate between screens
- Display notifications or dialogs
- Provide audible or haptic feedback on supported devices
- Perform data validation
- Execute workflows or complex operations
Available Actionsβ
CXTMS provides the following actions:
- setVariables - Sets variables in the UI context
- setActionsVariables - Sets variables specifically for use within actions
- setParams - Sets parameters for the current Screen or Dialog (Query String)
- setStore - Updates values in the application's store
- setFields - Sets values for form fields
- query - Executes a GraphQL query
- mutation - Executes a GraphQL mutation
- workflow - Executes a predefined workflow
- navigate - Navigates to a different route
- navigateBackOrClose - Navigates back or closes the current dialog
- notification - Displays a notification message
- dialog - Opens a dialog component
- confirm - Shows a confirmation dialog
- setValue - Sets the value of a specific form field
- if - Conditionally executes actions
- validateForm - Validates the current form
- validateStore - Validates store variables
- fileUpload - Uploads a file via the picker or programmatically (e.g. camera captures)
- fileDownload - Initiates a file download
- refresh - Recomputes a component from its current values
- reload - Refetches and replaces a form's values
- setLocalStorage - Sets a value in local storage
- setSessionStorage - Sets a value in session storage for the current browser tab
- forEach - Iterates over an array and executes actions
- consoleLog - Logs a message to the console
- submitForm - Submits a form
- resetDirtyState - Resets the dirty state of a form
- setAIContext - Sets the AI context for the current screen
- createNewAIChat - Creates a new AI chat
- openBarcodeScanner - Opens a barcode scanning UI and handles scan events
- submitFormToExternalUrl - Submits form data to an external HTTP endpoint
- sound - Plays a short audible feedback cue
- vibrate - Triggers device haptic feedback when supported
Let's explore each action in detail:
1. setVariablesβ
Sets variables in the UI context.
Attributes:
- [variable_name]: The name and value of each variable to set
- setVariables:
myForm.firstName: "John"
myForm.lastName: "Doe"
2. setActionsVariablesβ
Sets variables specifically for use within actions.
Attributes:
- [variable_name]: The name and value of each variable to set
- setActionsVariables:
firstName: "John"
lastName: "Doe"
3. setParamsβ
Sets parameters for the current Screen or Dialog.
Attributes:
- [param_name]: The name and value of each parameter to set
- setParams:
search: "John"
filter: "active"
4. setStoreβ
Updates values in the application's store.
Attributes:
- [store_path]: The path and new value to set in the store
- setStore:
myForm.firstName: "John"
myForm.lastName: "Doe"
5. setFieldsβ
Sets values for form fields.
Attributes:
- [field_name]: The name and new value for each form field
- setFields:
firstName: "John"
lastName: "Doe"
6. queryβ
Executes a GraphQL query.
Attributes:
- command: The GraphQL query string
- variables: An object containing query variables
- onSuccess: Actions to perform if the query is successful
- onError: Actions to perform if the query fails
- query:
command: "query { users { id name email }}"
variables: {}
onSuccess:
- setStore:
users: "{{ result.users }}"
onError:
- notification:
message: "Error fetching users"
7. mutationβ
Executes a GraphQL mutation.
Attributes:
- command: The GraphQL mutation string
- variables: An object containing mutation variables
- dirtyOnly: Submit only modified form fields (optional)
- onSuccess: Actions to perform if the mutation is successful
- onError: Actions to perform if the mutation fails
- mutation:
command: "mutation CreateUser($name: String!) { createUser(name: $name) { id name } }"
variables:
name: "John Doe"
onSuccess:
- notification:
message: "User created successfully"
onError:
- notification:
message: "Error creating user"
dirtyOnly β Partial Updatesβ
The dirtyOnly option filters mutation variables to include only form fields that were actually modified by the user. This is useful for PATCH-style updates where you want to avoid sending unchanged fields.
dirtyOnly accepts three formats:
| Value | Behavior |
|---|---|
true | Filter the entire variables object |
string | Dot-notation path to filter (e.g., "input.values") β keys outside the path are preserved |
object | { path: string, alwaysInclude?: string[] } β filter at path, but always include specified keys |
Example β Filter entire variables:
- mutation:
command: "mutation UpdateOrder($input: OrderInput!) { updateOrder(input: $input) { id } }"
variables:
input: "{{ form }}"
dirtyOnly: true
Example β Filter at a specific path:
- mutation:
command: "mutation UpdateOrder($id: Int!, $values: OrderValuesInput!) { updateOrder(id: $id, values: $values) { id } }"
variables:
id: "{{ id }}"
values: "{{ form }}"
dirtyOnly: "values"
Example β Filter with always-included keys:
- mutation:
command: "mutation UpdateContact($input: ContactInput!) { updateContact(input: $input) { id } }"
variables:
input: "{{ form }}"
dirtyOnly:
path: "input"
alwaysInclude:
- contactId
- organizationId
dirtyOnly works with React Hook Form's dirtyFields tracking. It only has effect when the mutation is triggered from within a form component. Fields that the user has not interacted with are excluded from the mutation variables.
8. workflowβ
Executes a predefined workflow.
Attributes:
- workflowId: The unique identifier of the workflow to execute
- inputs: An object containing input values for the workflow
- onSuccess: Actions to perform if the workflow completes successfully
- onError: Actions to perform if the workflow fails
- workflow:
workflowId: "f07103ce-d0b4-40fe-8a59-33eb851c8691"
inputs:
name: "John"
onSuccess:
- notification:
message: "Workflow completed"
onError:
- notification:
message: "Workflow failed"
9. navigateβ
Navigates to a different route in the application.
Attributes:
- to: The route path to navigate to
- navigate:
to: "/dashboard"
10. navigateBackOrCloseβ
Navigates back to the previous route or closes the current dialog.
Attributes:
- result: The result to pass back when closing a dialog (optional)
- navigateBackOrClose:
result: false
11. notificationβ
Displays a notification message.
Attributes:
- message: The notification message (can be localized)
- variant: The style variant of the notification (e.g., success, error, warning)
- notification:
message:
en-US: "This is a notification"
variant: success
12. dialogβ
Opens a dialog component.
Dialogs are responsive. Below the sm breakpoint they open full-screen, keep the title and inset close button visible, and scroll only the content area. On larger screens, a dialog without an explicit props.sx.width or props.sx.maxWidth uses a responsive width capped at 85% of the viewport or 1800 px. An explicitly configured width or maximum width continues to take precedence on desktop.
onClose templates can read both the caller's store and values written inside the dialog. Dialog values take precedence when names collide. This makes it possible to reload only after work actually occurred inside the dialog:
onClose:
- if: "{{ result || workflowExecuteForm.showErrorGrid }}"
then:
- reload: orderForm
The dialog store is available for every close path, including navigateBack, the close button, and backdrop dismissal. result remains undefined when the dialog closes without reporting one.
Attributes:
- name: A unique identifier for the dialog
- props: Properties to pass to the dialog component
- onClose: Actions to perform when the dialog is closed
- component: The definition of the dialog component
- dialog:
name: createDialog
props:
title:
en-US: "Create New Item"
onClose:
- setStore:
myForm.firstName: "{{ result.firstName }}"
component:
# Dialog component definition here
13. confirmβ
Shows a confirmation dialog (available for form fields only).
Attributes:
- title: The title of the confirmation dialog
- message: The message to display in the dialog
- onConfirm: Actions to perform if the user confirms
- onCancel: Actions to perform if the user cancels
- confirm:
title: "Delete User"
message: "Are you sure you want to delete this user?"
onConfirm:
- mutation:
command: "mutation DeleteUser($id: ID!) { deleteUser(id: $id) }"
variables:
id: "{{ userId }}"
onCancel:
- notification:
message: "Deletion cancelled"
14. setValueβ
Sets the value of a specific form field (available for form fields only).
Attributes:
- field: The name of the field to set
- value: The new value for the field
- setValue:
field: "myForm.firstName"
value: "John"
15. ifβ
Conditionally executes actions based on a specified condition.
Attributes:
- condition: The condition to evaluate
- then: Actions to perform if the condition is true
- else: Actions to perform if the condition is false
- if: "{{ eval myForm.firstName === 'John' }}"
then:
- notification:
message: "Hello John!"
else:
- notification:
message: "Hello {{ myForm.firstName }}"
16. validateFormβ
Validates the current form fields against a specified schema or the default form schema.
Attributes:
- validationSchema: An object defining the validation rules for form fields. If not provided, the form will be validated against the default form schema.
- validateForm:
validationSchema: # optional
firstName:
required:
message: "First Name is required"
min:
length: 2
message: "First Name should be at least 2 characters"
17. validateStoreβ
Validates store variables against a specified schema.
Attributes:
- validationSchema: An object defining the validation rules for store variables
- validateStore:
validationSchema:
"myForm.email":
required:
message: "Email is required"
email:
message: "Invalid email format"
18. fileUploadβ
Uploads a file to storage. Supports two forms: an interactive picker (the user selects a file) and a programmatic upload (the file is supplied inline, e.g. from a camera capture).
Interactive picker formβ
Opens a native file-picker dialog. The user selects a file, which is then uploaded.
Attributes:
accept: File types to accept (e.g.,".jpg,.png"). Honored by the interactive picker.onSuccess: Actions to perform when the upload succeeds. Receivesresultwith{ url, fileUrl, fileName }.onError: Actions to perform if the upload fails.
maxSize and multiple are listed for completeness but are not honored by the current implementation. File-size validation and multi-file selection are planned for a future release.
- fileUpload:
accept: ".jpg,.png"
onSuccess:
- notification:
message: "File uploaded: {{ result.fileName }}"
onError:
- notification:
message: "Upload failed"
Programmatic form (camera / data-URL upload)β
When file is provided, the action uploads that data directly without opening the file picker. This is the standard pattern for uploading camera captures.
Attributes:
file: A data URL to upload (e.g."{{ result.data }}"from anonCapturehandler).name: The target file name on the server (e.g."photo-{{ store.orderId }}.jpg").onSuccess: Actions to perform when the upload succeeds. Receivesresultwith{ url, fileUrl, fileName }.onError: Actions to perform if the upload fails.
- fileUpload:
file: "{{ result.data }}"
name: "photo-{{ store.orderId }}.jpg"
onSuccess:
- setStore:
photoUrl: "{{ result.fileUrl }}" # persist fileUrl β it is the durable URL
- notification:
message: "Photo uploaded"
variant: success
onError:
- notification:
message: "Upload failed"
variant: error
onSuccess result payloadβ
| Field | Description |
|---|---|
fileUrl | Durable URL β persist this value. Suitable for storing on records and sharing with users. |
url | Temporary credentialed upload URL. Do not store this; it expires shortly after upload. |
fileName | The file name used on the server. |
19. fileDownloadβ
Initiates a file download from a specified URL.
Attributes:
- url: The URL of the file to download
- fileDownload:
url: "https://example.com/files/document.pdf"
20. refreshβ
Refreshes the current screen or a specific component. Optionally pass highlight options to control how the target DataGrid highlights new/updated rows for this specific refresh.
Attributes:
- componentName: The name of the component to refresh (optional)
- options: Highlight options that override the target DataGrid's defaults for this refresh (optional)
- highlightNew: Whether to highlight newly added rows (boolean)
- highlightUpdated: Whether to highlight updated rows (boolean)
- highlightForRefreshes: Per-row TTL β number of refresh cycles a highlight persists (number)
# Simple refresh (backward compatible)
- refresh: "pickupOrders"
# Refresh with highlight options
- refresh: "pickupOrders"
options:
highlightNew: true
highlightForRefreshes: 3
# Refresh with highlighting disabled
- refresh: "pickupOrders"
options:
highlightNew: false
highlightUpdated: false
Options passed here override the target DataGrid's grid-level highlight defaults for this specific refresh cycle. If no options are provided, the DataGrid uses its own configured defaults. See DataGrid β Change Tracking & Row Highlights for details.
21. reloadβ
Refetches a form's initialValues and replaces its current values without remounting the component tree. Unlike refresh, which recomputes from values already held by the form, reload fetches the source of truth again and discards unsaved edits.
- reload: orderForm
- reload: [orderForm, orderLinesForm]
A form listens on props.reloadHandler, then props.refreshHandler, then its own name. Reusing a refresh handler name is safe because reload and refresh use independent signals.
Reload is fire-and-forget. Query-backed values bypass the cache, missing keys are cleared, and the reloaded values become the clean dirty-state baseline. Use setFields instead when a dialog already returns the exact changed values.
21. setLocalStorageβ
Sets a value in the browser's local storage.
Attributes:
- key: The key to use in local storage
- value: The value to store
- setLocalStorage:
key: "userPreferences"
value: "{{ userPreferences }}"
22. setSessionStorageβ
Sets a value in the browser's session storage for the current tab. The value is template-parsed with the current action data, store values, and form values before it is written.
Attributes:
- key: The unscoped session storage key to write
- value: The value to store
- onSuccess: Actions to run after the value is stored
- onError: Actions to run if the write fails
- setSessionStorage:
key: "isDevMode"
value: "true"
onSuccess:
- refresh: "developerToolbar"
setSessionStorage writes unscoped keys. It is intended for tab-scoped framework flags and transient UI state, not durable user preferences.
23. forEachβ
Iterates over an array and executes specified actions for each item.
Attributes:
- items: The array to iterate over
- item: The name to use for the current item in the iteration
- actions: The actions to perform for each item
- forEach:
items: "{{ myForm.items }}"
item: "currentItem"
actions:
- notification:
message: "Processing {{ currentItem.name }}"
24. consoleLogβ
Logs a message to the console.
Attributes:
- message: The message to log
- level: The log level (optional, defaults to "info". Other levels: "error", "warn", "debug", "log")
- consoleLog:
level: "info" # optional
message: "Hello World"
- consoleLog: "Hello World" # simplified version
25. submitFormβ
Submits a form. If the form name is not provided, the action will submit the form in which the button is placed. It's recommended to use submit form since it's reset dirty state of the form.
Attributes:
- formName: The name of the form to submit (optional, defaults to the name of the form component, in which the button is placed)
- submitForm:
formName: "myForm"
26. resetDirtyStateβ
Resets the dirty state of a form to false, indicating the form no longer has unsaved changes. Use this action when you want to programmatically mark a form as clean without submitting it, such as after a custom save operation or when discarding changes.
Attributes:
- formName: The name of the form to reset (optional, defaults to the current form context)
- resetDirtyState:
formName: "myForm"
Common use cases:
- After a successful custom save operation that doesn't use
submitForm - When implementing a "Discard Changes" button
- After programmatically reverting form values to their original state
Example with mutation:
- mutation:
command: "mutation SaveDraft($input: DraftInput!) { saveDraft(input: $input) { id } }"
variables:
input: "{{ myForm }}"
onSuccess:
- resetDirtyState:
formName: "myForm"
- notification:
message: "Draft saved"
variant: success
Best Practicesβ
- Use meaningful names for your actions to improve readability.
- Group related actions together for better organization.
- Use conditional actions (if) to handle different scenarios.
- Utilize setActionsVariables for temporary data that's only needed within the action sequence.
- Always handle both success and error cases in asynchronous actions (query, mutation, workflow).
- Use validateForm before submitting data to ensure data integrity.
- Leverage the forEach action for batch operations on arrays.
- Use
soundandvibratesparingly for high-signal events (scan success, mutation errors) rather than every button click. - Pair device feedback with a visual cue (
notification) so users who disable sound or vibration still get confirmation.
setAIContextβ
Sets the AI context for the current screen.
Attributes:
- entities: The entities to set in the AI context
- quickQuestions: The common questions to set in the AI context
- setAIContext:
entities:
- entityType: "{{ entityType }}"
entityName: "{{ entityName }}"
entityId: "{{ entityId }}"
displayText: "{{ entityName }}"
quickQuestions:
- "What is the name of the item?"
- "What is the description of the item?"
- "What is the price of the item?"
createNewAIChatβ
Creates a new AI chat. This action is used to create a new AI chat for the current screen. If the AI chat is closed, the action will open an AI window.
Attributes:
- title: The title of the AI chat
- topic: The topic of the AI chat
- entities: The entities to set in the AI chat
- quickQuestions: The quick questions to set in the AI chat
- question: The question to set in the AI chat
- autoExecute: Whether to automatically execute the AI chat (optional, defaults to false)
- createNewAIChat:
title: "My AI Chat"
topic: "{{ entityName }}"
entities:
- entityType: "{{ entityType }}"
entityName: "{{ entityName }}"
entityId: "{{ entityId }}"
displayText: "{{ entityName }}"
quickQuestions:
- "What is the name of the item?"
question: "What is the name of the item?"
autoExecute: true # optional, defaults to false
28. openBarcodeScannerβ
Opens a dedicated barcode scanning UI. Supports single-scan (default) and multi-scan flows. In multi-scan mode, the UI remains open until the user completes scanning or a maxBarcodes limit is reached.
Attributes:
- title: Optional title text for the scanner UI
- minBarcodeLength: Minimum length for recognized barcodes (defaults to 1 if omitted)
- allowMultiple: Whether to allow scanning multiple barcodes before closing (optional, defaults to false)
- maxBarcodes: Maximum number of barcodes to collect in multi-scan mode (optional). When set and
allowMultipleis true, the dialog auto-closes after this many scans - onScan: Actions to execute after each successful scan; receives
result.dataand, when available,result.format - onComplete: Actions to execute when the scanner UI finishes multi-scan and closes (user taps Done or
maxBarcodesreached). Receivesresult.barcodes(array of strings) - onClose: Actions to execute when the scanner UI is closed without completing (for example, user cancels). May include
result.barcodescollected so far if any
Notes:
- Single-scan (default): the dialog closes immediately after the first scan;
onScanruns once andonCompleteis not used. - Multi-scan (
allowMultiple: true):- Without
maxBarcodes, users close the dialog via the UI when finished;onCompletereceives all collected barcodes asresult.barcodes. - With
maxBarcodes, the dialog auto-closes when the limit is reached;onCompletereceives the final list asresult.barcodes.
- Without
result.formatmay vary by platform; guard its usage.
Single-scan example:
- openBarcodeScanner:
title: "Scan Item"
minBarcodeLength: 8
onScan:
- setStore:
scannedBarcode: "{{ result.data }}"
- notification:
message: "Scanned: {{ result.data }}"
onClose:
- notification:
message: "Scanner closed"
Multi-scan until user completes:
- openBarcodeScanner:
title: "Scan multiple items"
allowMultiple: true
minBarcodeLength: 8
onScan:
- notification:
message: "Captured: {{ result.data }}"
onComplete:
- setStore:
scannedBarcodes: "{{ result.barcodes }}"
- notification:
message: "Collected {{ result.barcodes.length }} barcodes"
Multi-scan with maximum count (auto-close):
- openBarcodeScanner:
title: "Scan up to 5 items"
allowMultiple: true
maxBarcodes: 5
minBarcodeLength: 8
onScan:
- notification:
message: "Added: {{ result.data }}"
onComplete:
- setStore:
scannedBarcodes: "{{ result.barcodes }}"
- notification:
message: "Scanning completed ({{ result.barcodes.length }} items)"
Conditional handling by format (single or multi-scan):
- openBarcodeScanner:
title: "Scan or log"
minBarcodeLength: 10
allowMultiple: true
maxBarcodes: 3 # optional
onScan:
- if:
condition: "{{ eval result.format === 'QR_CODE' }}"
then:
- navigate:
to: "/qr-details"
else:
- mutation:
command: "logBarcodeScan"
variables:
barcodeData: "{{ result.data }}"
format: "{{ result.format }}"
- notification:
message: "Barcode logged"
onComplete:
- notification:
message: "Finished scanning"
Integrating with a button:
component: button
props:
text: "Scan barcodes"
onClick:
- openBarcodeScanner:
title: "Scan Items"
allowMultiple: true
maxBarcodes: 10
minBarcodeLength: 8
onScan:
- consoleLog:
message: "Scanned: {{ result.data }}"
onComplete:
- setStore:
scannedBarcodes: "{{ result.barcodes }}"
- refresh: "dataGrid"
29. submitFormToExternalUrlβ
Submits form data to an external HTTP(S) endpoint. You can submit the values of an existing form by name (optionally remapping or excluding fields) or provide an explicit fields object. Useful for webhooks, public HTTP echo services, and simple REST endpoints.
Attributes:
- url: The destination URL to send the data to (supports template expressions)
- method: The HTTP method to use (e.g., "POST"). Defaults to "POST" if omitted
- target: Browser target for the submission (e.g., "_blank" to open a new tab)
- formName: Name of the form whose values will be submitted
- fields: Explicit key-value object to send instead of reading from a form
- formData: Object of key-value pairs to submit (supports template expressions). When used with formName, keys map to external field names and override/augment collected form values
- excludeFields: Array of internal field names to exclude from submission (applies when using formName)
- beforeSubmit: Actions to execute before the request is sent
- onSuccess: Actions to execute after a successful submission
- onError: Actions to execute if submission fails
Notes:
- Use either
formName(optionally withformData/excludeFields) or providefieldsexplicitly. url,fieldsvalues, and mappings support template expressions.- Setting
target: "_blank"opens the response in a new browser tab.
Simple submission to httpbin.org:
- submitFormToExternalUrl:
url: "https://httpbin.org/post"
method: "POST"
target: "_blank"
formName: "simpleTestForm"
onSuccess:
- notification:
type: success
message:
en-US: "Form submitted! Check the new browser tab to see the response."
With field mapping and exclusions:
- submitFormToExternalUrl:
url: "https://httpbin.org/post"
method: "POST"
target: "_blank"
formName: "mappingTestForm"
formData:
first_name: "{{ mappingTestForm.firstName }}"
last_name: "{{ mappingTestForm.lastName }}"
company: "{{ mappingTestForm.companyName }}"
phone: "{{ mappingTestForm.phoneNumber }}"
excludeFields:
- "internalNotes"
- "tempData"
onSuccess:
- notification:
type: success
message:
en-US: "Form submitted with field mapping! Internal fields excluded."
Dynamic URL (Webhook.site) with explicit fields:
- submitFormToExternalUrl:
url: "{{ webhookTestForm.webhookUrl }}"
method: "POST"
target: "_blank"
fields:
test_data_1: "{{ webhookTestForm.testData1 }}"
test_data_2: "{{ webhookTestForm.testData2 }}"
timestamp: "{{ format today 'yyyy-MM-dd HH:mm:ss' }}"
user_agent: "TMS Frontend Web - Test"
onSuccess:
- notification:
type: success
message:
en-US: "Submitted! Check webhook.site to see your data in real-time."
Complete flow with validation, pre-submit, and success handling:
# Step 1: Validate form (usually triggered before submit)
- validateForm:
formName: completeFlowForm
validationSchema:
customerName:
type: string
required:
message:
en-US: "Customer name is required"
customerEmail:
type: string
required:
message:
en-US: "Customer email is required"
productName:
type: string
required:
message:
en-US: "Product name is required"
# Step 2: Submit externally with mapping and lifecycle hooks
- submitFormToExternalUrl:
url: "https://httpbin.org/post"
method: "POST"
target: "_blank"
formName: "completeFlowForm"
formData:
order_id: "{{ completeFlowForm.orderNumber }}"
date_created: "{{ completeFlowForm.orderDate }}"
customer_full_name: "{{ completeFlowForm.customerName }}"
customer_email_address: "{{ completeFlowForm.customerEmail }}"
product_name: "{{ completeFlowForm.productName }}"
qty: "{{ completeFlowForm.quantity }}"
total_price: "{{ completeFlowForm.totalAmount }}"
order_notes: "{{ completeFlowForm.notes }}"
beforeSubmit:
- setStore:
lastSubmission:
orderNumber: "{{ completeFlowForm.orderNumber }}"
status: "submitting"
timestamp: "{{ today }}"
- notification:
type: info
message:
en-US: "Processing order {{ completeFlowForm.orderNumber }}..."
onSuccess:
- setStore:
lastSubmission:
orderNumber: "{{ completeFlowForm.orderNumber }}"
status: "submitted"
timestamp: "{{ today }}"
- notification:
type: success
message:
en-US: "Order {{ completeFlowForm.orderNumber }} submitted successfully!"
- navigateBackOrClose: {}
30. soundβ
Plays a short audible cue on the client device. Use it to confirm successful scans, validation outcomes, or other high-signal eventsβespecially in warehouse and mobile workflows where the user may not be looking at the screen.
Requires @cxtms/cx-schema 1.9.80+ for YAML validation. Older web and mobile client releases ignore these actions (safe no-op), so modules using them remain backward compatible.
Attributes:
- type: Preset sound to play (optional, defaults to
success). Supported values:success,error,warning,scan - volume: Playback volume from
0to1(optional, defaults to1)
Notes:
- On mobile, users can independently enable sound for successful and failed scans and
choose low, medium, or high cue volume in Profile & Settings.
errorandwarninguse the failed-scan preference; other presets use the successful-scan preference. These preferences are stored on the device and do not change the YAML action contract. - On iOS, enabled scan cues play even when the silent switch is on. On Android, playback retains the platform audio defaults.
- On web/desktop, playback uses the browser audio layer when available; unsupported environments fail silently.
- Respects the device mute switch and browser autoplay policiesβif audio cannot play, the action completes without error and remaining actions in the chain continue.
- Does not display UI; combine with
notificationwhen users also need a visible message.
Simple preset (shorthand):
- sound: success
Object form with volume:
- sound:
type: error
volume: 0.8
Barcode scan success feedback:
- openBarcodeScanner:
title: "Scan Item"
minBarcodeLength: 8
onScan:
- sound: scan
- vibrate: success
- setStore:
scannedBarcode: "{{ result.data }}"
- notification:
message: "Scanned: {{ result.data }}"
type: success
Mutation error feedback:
- mutation:
command: "mutation ConfirmPick($id: Int!) { confirmPick(id: $id) { id } }"
variables:
id: "{{ pickId }}"
onSuccess:
- sound: success
- vibrate: success
onError:
- sound:
type: error
- vibrate:
type: error
- notification:
message: "Could not confirm pick"
type: error
31. vibrateβ
Triggers device vibration or haptic feedback. Use it alongside or instead of sound on phones and tabletsβfor example, to confirm a scan without relying on audio in noisy environments.
Requires @cxtms/cx-schema 1.9.80+ for YAML validation. Older web and mobile client releases ignore these actions (safe no-op), so modules using them remain backward compatible.
Attributes:
- type: Preset haptic pattern (optional, defaults to
success). Supported values:success,error,warning,light,medium,heavy - pattern: Custom vibration sequence as an array of milliseconds (optional). Follows the Vibration API convention: even-index elements (
0,2,4, β¦) are vibration durations; odd-index elements (1,3,5, β¦) are pauses. When set, overridestype. - duration: Single vibration pulse length in milliseconds (optional). Used when
patternis omitted and you need a simple one-shot pulse instead of a named preset.
Notes:
- On mobile,
success,error, andwarningmap to notification-style haptics;light,medium, andheavymap to impact-style haptics. - Mobile users can independently disable vibration for successful and failed scans in
Profile & Settings.
errorandwarninguse the failed-scan preference; other presets, custom patterns, and durations use the successful-scan preference. Settings are local to the device, and disabled feedback still completes the action successfully. - On web, uses
navigator.vibrate()when the browser and device support it. - On desktop or unsupported clients, the action is a no-op and the action chain continues.
- Some platforms ignore vibration when the device is in silent mode or low-power mode.
Simple preset (shorthand):
- vibrate: success
Named impact preset:
- vibrate:
type: heavy
Custom pattern (double pulse):
- vibrate:
pattern: [100, 50, 100]
Single-duration pulse:
- vibrate:
duration: 200
Multi-scan workflow with feedback:
- openBarcodeScanner:
title: "Scan up to 5 items"
allowMultiple: true
maxBarcodes: 5
minBarcodeLength: 8
onScan:
- sound: scan
- vibrate: light
- notification:
message: "Added: {{ result.data }}"
onComplete:
- sound: success
- vibrate: success
- setStore:
scannedBarcodes: "{{ result.barcodes }}"
Button click with subtle feedback:
component: button
props:
text: "Confirm Pick"
onClick:
- vibrate: light
- mutation:
command: "mutation ConfirmPick($id: Int!) { confirmPick(id: $id) { id } }"
variables:
id: "{{ pickId }}"
onSuccess:
- sound: success
- vibrate: success
- notification:
message: "Pick confirmed"
type: success
Related Topicsβ
- openBarcodeScanner β common source event for scan feedback
- notification β pair with sound/vibrate for visible confirmation
- if β branch feedback by scan result or validation outcome
- Barcode Scanner Component β scanner component and
onScancontext
Mobile storage and downloadsβ
On mobile, fileDownload opens the resolved URL through the operating system instead of writing directly to an app-owned download directory. Invalid or unresolved URLs fail the action. setLocalStorage is an alias of persistent setStorage; values are scoped and made available to {{ localStorage <key> }} templates. Secure storage entries are not exposed through that template namespace.