How Events and Actions Work
You make an app interactive by attaching events to a component (or to the page itself) and giving each event one or more actions. An event is the trigger — a click, a value change, a page load. An action is what runs in response — navigate, run a data source, set a variable, open a modal.
You configure interactions in a component's Interactions section using Add Interaction. For page-wide triggers, go to Page Settings → Events. Actions on the same event run in order, top to bottom.
Note: Where to configure: Application builder → select a component → Properties panel → Interactions tab → Add Interaction.
Event Node Anatomy
Internally, each event-action pair is stored as a node in the component's events array. Understanding this shape helps when reading generated configurations or debugging unexpected behavior.
JSON — event node structure
{ "eventType": "onClick", "id": "Evt_6VRjl", "action": { "actionType": "showNotification", "payload": { "type": "success", "title": "Saved successfully" }, "id": "act_674C5" } }
Key facts about the node shape:
eventType — the exact camelCase event key (e.g.
onClick,onChange,onSubmit). The click event is alwaysonClick— lowercase "on", uppercase "C".id — platform-minted identifier beginning with
Evt_orevt_.action — a single action node. Multiple actions on the same event are sibling entries in the array sharing the same
eventType, executed in array order.
Event Types
Each component exposes the events that make sense for it. The table below covers the most common ones.
| Event Key | Trigger | Available On |
| onClick | User clicks the component | Most interactive components |
| onChange | Value changes in an input or select | Text Input, Select, Checkbox, Date Picker, etc. |
| onSubmit | Form is submitted | Form block |
| onMouseEnter | Cursor moves over the component | Most visible blocks |
| onOpen / onClose | Component opens or closes | Modal, Drawer, Bottom Sheet |
| onFocus / onBlur | Component gains or loses keyboard focus | Input fields |
| onRowSelect | A table row is selected | Table block |
| onPageChange | Table pagination changes | Table block |
| onCellClick / onCellValueChange | A table cell is clicked or its value changes | Table block |
| onStepChange | Active step changes | Stepper block |
| onFilterChange | Filter criteria change | Filters component |
| onFileUploaded | File upload completes | File Upload block |
| onDataPointClick | User clicks a data point on a chart | Chart blocks |
| onPageLoad | Page finishes mounting | Page-level events |
| onPageFocus | Browser tab regains focus | Page-level events |
| onKeyboardShortcut | A configured keyboard shortcut fires | Page-level Keyboard Shortcuts |
| onReceiveMQTTEvent | MQTT message received on a subscribed topic | Page-level events |
| onTimerExpire | Timer block countdown ends | Timer block |
| onSuccess / onFailure | Data source completes or errors | Data source events |
Action Catalog
The action picker groups available actions by category. The full list is shown below.
Data Actions
| Action | What it does |
| controlDataSource | Re-runs or refreshes a data source on demand |
| controlBlockMethod | Calls a named method on a target block (submit form, scroll into view, etc.) |
| triggerFunction | Executes a manual page or app function |
| executeScript | Runs arbitrary JavaScript inline |
Navigation Actions
| Action | What it does |
| navigate | Go to an external URL or raw path |
| navigateToPage | Go to another page in the same app by page ID |
| navigateBack | Go back in browser history |
| setURLParameters | Update the current page's query string without navigating |
| openShareDialog | Open the OS share sheet or copy a shareable link |
UI Control Actions
| Action | What it does |
| controlModal | Open or close a Modal block |
| controlDrawer | Open or close a Drawer block |
| controlBottomSheet | Open or close a Bottom Sheet block |
| controlNavigationDrawer | Open or close the app's navigation drawer |
| showNotification | Display a toast/snackbar notification |
| submitForm | Programmatically submit a Form block |
| controlWalkthrough | Start, stop, or advance a guided walkthrough |
| controlMediaViewer | Open or close a media viewer overlay |
State Actions
| Action | What it does |
| setPageVariable | Read and write a page or app variable |
| setInterfaceSessionStorage | Write a value to browser session storage |
| setLocalStorage | Write a value to browser local storage |
User & Data Actions
| Action | What it does |
| controlUser | Logout, change language/theme, refresh user context |
| copyToClipboard | Write a value to the clipboard |
| exportData | Export records to CSV, XLS, or XLSX |
| exportBlock | Export a block as an image or PDF |
| downloadFile | Download a file from a URL |
| importObjects | Import records from a file upload |
Event Actions
| Action | What it does |
| emitPageEvent | Emit a named event that other components can listen for |
| sendMQTTEvent | Publish a message to an MQTT topic |
Action Node Structure
Every action — regardless of type — follows the same node shape:
JSON — full action node
{ "actionType": "showNotification", "payload": { "type": "success", "title": "Record saved", "description": "Changes were applied." }, "id": "act_674C5", "runCondition": { "type": "filter", "payload": { "operator": "AND", "filters": [ { "property": "{{ variables.isAdmin }}", "filter": { "operator": "EQUAL", "value": "true" } } ] } }, "onSuccessActions": [ { "actionType": "navigate", "payload": { "path": "/dashboard" }, "id": "act_abc1" } ], "onErrorActions": [ { "actionType": "showNotification", "payload": { "type": "error", "title": "Failed" }, "id": "act_abc2" } ] }
| Field | Type | Required | Description |
| actionType | string | Required (required) | The exact machine ID of the action (e.g. showNotification, navigate). |
| payload | object | Required (required) | Action-specific parameters. Fields differ per actionType. |
| id | string | Required (required) | Platform-minted identifier (act_…). Always present in practice. |
| runCondition | ConditionObject | Optional (optional) | If provided, the action only runs when this condition evaluates to true. Must be an object ({ "type": "boolean", "payload": true } or a filter group) — never a raw string. |
| onSuccessActions | Action[] | Optional (optional) | Actions to run after this action succeeds. One level deep only. |
| onErrorActions | Action[] | Optional (optional) | Actions to run if this action fails. One level deep only. |
| delayDuration | number (ms) | Optional (optional) | Milliseconds to wait before running. Used with executionType: "delay". |
| executionType | string | Optional (optional) | delay, debounce, or throttle. Controls rate-limiting behavior. |
Sequential vs Callback Execution
UnifyApps gives you three ways to chain actions. Choose based on whether subsequent actions depend on the prior action's success:
1. Sequential Steps (same event)
When you add multiple actions to one event, they run as sibling entries in array order. Use this for "do A then B" where B does not depend on whether A succeeded.
Pattern — sequential actions on onClick
{ "events": [ { "eventType": "onClick", "id": "Evt_001", "action": { "actionType": "setPageVariable", "payload": { "variableId": "var_abc", "value": "active" }, "id": "act_001" } }, { "eventType": "onClick", "id": "Evt_002", "action": { "actionType": "showNotification", "payload": { "type": "success", "title": "Done" }, "id": "act_002" } } ] }
2. Success/Failure Callbacks
Use onSuccessActions and onErrorActions on an action node when the follow-up only makes sense after the parent succeeds or fails. For example: save a record → on success navigate away, on failure show an error.
Warning: One level only. Callback actions cannot carry their own onSuccessActions/onErrorActions. The builder enforces this limit. For a second stage, use a sibling event entry instead.
3. Data Source Events
When you want to react to an async data source completing, put the follow-up actions on the data source's own onSuccess / onFailure events — not inside the action that triggered the data source. This is the correct pattern for "call an API, then show a success toast."
Conditional Execution
Each action supports a runCondition — a condition object that gates whether the action runs. If the condition evaluates to false, the action is skipped without error.
The condition object takes one of two forms:
Form 1 — boolean literal
{ "type": "boolean", "payload": true }
Form 2 — filter group (role-based gate)
{ "type": "filter", "payload": { "operator": "AND", "filters": [ { "property": "{{ user.role }}", "filter": { "operator": "EQUAL", "value": "admin" } } ] } }
Execution Strategy: Delay, Debounce, Throttle
Each action can have an execution strategy that rate-limits when it fires:
| Strategy | Behavior | Use Case |
| delay | Waits delayDuration ms, then runs | Show a tooltip after a 500 ms hover |
| debounce | Resets the timer on each trigger; runs after the quiet period | Search-as-you-type — only fire after typing stops |
| throttle | Runs at most once per delayDuration window | Scroll events or rapid button clicks |
Managing Events and Actions
In the builder's Interactions panel you can:
Reorder actions by dragging within the same event.
Copy an action to reuse its configuration elsewhere.
Disable an event or action without deleting it — disabled items are skipped at runtime and shown dimmed with a tooltip. Use this to temporarily switch off behavior while testing.
Add a confirmation dialog to require user acknowledgment before a destructive action runs.
Tip: Tip: Put a navigate or navigateToPage action last in a sequential chain. The runner continues executing later nodes even after a same-tab navigation (it does not cancel the loop), so a navigate mid-chain can lead to unexpected behavior.
End-to-End Example: Save & Navigate
A button that saves a record, shows a success toast, then navigates to a list page — or shows an error on failure:
Complete event handler — save button
{ "eventType": "onClick", "id": "Evt_saveBtn", "action": { "actionType": "controlDataSource", "payload": { "dataSourceId": "ds_saveRecord", "method": "trigger" }, "id": "act_save", "onSuccessActions": [ { "actionType": "showNotification", "payload": { "type": "success", "title": "Record saved" }, "id": "act_toast" }, { "actionType": "navigateToPage", "payload": { "pageId": "e_listPage", "target": "_self", "history": "push" }, "id": "act_nav" } ], "onErrorActions": [ { "actionType": "showNotification", "payload": { "type": "error", "title": "Save failed", "description": "{{ ds_saveRecord.error }}" }, "id": "act_err" } ] } }
Frequently Asked Questions
Can multiple actions run when a single event fires?
Yes. You can attach a sequence of actions to any event — they run one after the other, in the order you configure them. Each action's onSuccessActions and onErrorActions let you branch the chain based on whether the preceding step succeeded or failed.
How do I prevent an action from running unless certain conditions are met?
Use the runCondition field on the action node. Set it to a filter-type condition object referencing the variables or data you want to check. The action is skipped (without stopping the chain) when the condition evaluates to false.
What is the difference between delay, debounce, and throttle?
Delay waits a fixed number of milliseconds before running the action once. Debounce collapses rapid successive firings — it resets the timer every time the event fires and only runs after the quiet period ends. Throttle runs at most once per interval regardless of how many times the event fires during that window.
Do actions in onSuccessActions run in parallel or in sequence?
In sequence, in the order listed. Each subsequent action waits for the previous one to complete (or fail). If you want actions to run truly in parallel, place them as siblings in the main action list rather than in a nested callback list.
What happens if an action without an onErrorActions block fails?
The error is swallowed silently and the chain stops at that point. To surface failures, add an onErrorActions block with a showNotification action or a variable write that records the error.