Actions
Complete reference for action definitions and step types
Overview
Actions define how your application responds to user interactions and events. Each action consists of a name and a sequence of steps that execute in order.
Action Definition
{
"name": "incrementCounter",
"steps": [
{ "do": "update", "target": "count", "operation": "increment" }
]
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| name | string | Yes | - | Unique identifier for the action. Used in event handlers. |
| steps | ActionStep[] | Yes | - | Array of steps to execute when the action is triggered. |
Set Step
Directly sets a state value.
{
"do": "set",
"target": "message",
"value": { "expr": "lit", "value": "Hello!" }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "set" | Yes | - | Step type identifier. |
| target | string | Yes | - | State field to modify. Use dot notation for nested paths. |
| value | Expression | Yes | - | Expression that evaluates to the new value. |
Examples
// Set a simple value
{ "do": "set", "target": "name", "value": { "expr": "lit", "value": "John" } }
// Set from event parameter
{
"do": "set",
"target": "inputValue",
"value": { "expr": "param", "name": "event", "path": "target.value" }
}
// Set nested property
{
"do": "set",
"target": "user.profile.name",
"value": { "expr": "lit", "value": "Jane" }
}Update Step
Applies a predefined operation to a state value.
{
"do": "update",
"target": "count",
"operation": "increment"
}| Name | Type | Required | Default | Description |
|---|
Operations
| Operation | Description | Requires Value | State Type |
|---|---|---|---|
increment | Adds to a number (default: 1) | No (optional value) | number |
decrement | Subtracts from a number (default: 1) | No (optional value) | number |
toggle | Flips a boolean value | No | boolean |
push | Appends to an array | Yes | list |
pop | Removes last element from array | No | list |
remove | Removes matching element from array | Yes | list |
replaceAt | Replaces element at a specific index | Yes (index + value) | list |
insertAt | Inserts element at a specific index | Yes (index + value) | list |
splice | Deletes and/or inserts at index | Yes (index + deleteCount, optional value) | list |
merge | Shallow-merges properties into an object | Yes | object |
Examples
// Increment counter
{ "do": "update", "target": "count", "operation": "increment" }
// Add item to array
{
"do": "update",
"target": "todos",
"operation": "push",
"value": { "expr": "state", "name": "newTodo" }
}
// Remove item from array
{
"do": "update",
"target": "items",
"operation": "remove",
"value": { "expr": "var", "name": "itemToRemove" }
}
// Remove last item
{ "do": "update", "target": "stack", "operation": "pop" }toggle
Flips a boolean state value. Ideal for toggles, modals, and visibility controls.
{ "do": "update", "target": "isOpen", "operation": "toggle" }| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "update" | Yes | - | Step type identifier. |
| target | string | Yes | - | State field to toggle (must be boolean). |
| operation | "toggle" | Yes | - | Toggle operation identifier. |
Note
The target state must be a boolean type. Using toggle on non-boolean values results in a compile-time error.
merge
Shallow-merges properties into an object state. Existing properties are preserved unless overwritten.
{
"do": "update",
"target": "form",
"operation": "merge",
"value": { "expr": "lit", "value": { "email": "user@example.com" } }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "update" | Yes | - | Step type identifier. |
| target | string | Yes | - | State field to merge into (must be object). |
| operation | "merge" | Yes | - | Merge operation identifier. |
| value | Expression | Yes | - | Object expression to merge into target. |
Merge Example
// State: form = { name: "John", email: "" }
{
"do": "update",
"target": "form",
"operation": "merge",
"value": { "expr": "lit", "value": { "email": "john@example.com", "phone": "123-456" } }
}
// Result: form = { name: "John", email: "john@example.com", phone: "123-456" }replaceAt
Replaces an element at a specific index in an array. Useful for editing list items in place.
{
"do": "update",
"target": "todos",
"operation": "replaceAt",
"index": { "expr": "var", "name": "index" },
"value": { "expr": "lit", "value": { "title": "Updated", "done": true } }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "update" | Yes | - | Step type identifier. |
| target | string | Yes | - | State field (must be list). |
| operation | "replaceAt" | Yes | - | ReplaceAt operation identifier. |
| index | Expression | Yes | - | Expression evaluating to the index to replace. |
| value | Expression | Yes | - | Expression evaluating to the new value. |
Tip
The replaceAt operation is commonly used inside each loops where the index is available, enabling inline editing of list items.
insertAt
Inserts an element at a specific index in an array. Elements at and after the index are shifted.
{
"do": "update",
"target": "items",
"operation": "insertAt",
"index": { "expr": "lit", "value": 0 },
"value": { "expr": "state", "name": "newItem" }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "update" | Yes | - | Step type identifier. |
| target | string | Yes | - | State field (must be list). |
| operation | "insertAt" | Yes | - | InsertAt operation identifier. |
| index | Expression | Yes | - | Expression evaluating to the index to insert at. |
| value | Expression | Yes | - | Expression evaluating to the value to insert. |
splice
Deletes and/or inserts items at a specific index. The most flexible list operation, mirroring JavaScript's Array.prototype.splice().
{
"do": "update",
"target": "items",
"operation": "splice",
"index": { "expr": "var", "name": "index" },
"deleteCount": { "expr": "lit", "value": 1 },
"value": { "expr": "lit", "value": ["replacement1", "replacement2"] }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "update" | Yes | - | Step type identifier. |
| target | string | Yes | - | State field (must be list). |
| operation | "splice" | Yes | - | Splice operation identifier. |
| index | Expression | Yes | - | Expression evaluating to the start index. |
| deleteCount | Expression | Yes | - | Expression evaluating to the number of elements to delete. |
| value | Expression | No | - | Expression evaluating to an array of items to insert (optional). |
Note
Set deleteCount to 0 to insert without deleting. Omit value to delete without inserting.
Fetch Step
Makes an HTTP request and handles the response.
{
"do": "fetch",
"url": { "expr": "lit", "value": "https://api.example.com/data" },
"method": "GET",
"result": "fetchedData",
"onSuccess": [
{ "do": "set", "target": "isLoading", "value": { "expr": "lit", "value": false } }
],
"onError": [
{ "do": "set", "target": "error", "value": { "expr": "param", "name": "error" } }
]
}| Name | Type | Required | Default | Description |
|---|
HTTP Methods
| Method | Description | Typical Use |
|---|---|---|
GET | Retrieve data | Fetching lists, details |
POST | Create new resource | Submitting forms, creating items |
PUT | Update existing resource | Editing items |
DELETE | Remove resource | Deleting items |
Complete Fetch Example
{
"name": "loadUsers",
"steps": [
{
"do": "set",
"target": "isLoading",
"value": { "expr": "lit", "value": true }
},
{
"do": "fetch",
"url": { "expr": "lit", "value": "https://api.example.com/users" },
"method": "GET",
"result": "users",
"onSuccess": [
{
"do": "set",
"target": "isLoading",
"value": { "expr": "lit", "value": false }
}
],
"onError": [
{
"do": "set",
"target": "isLoading",
"value": { "expr": "lit", "value": false }
},
{
"do": "set",
"target": "errorMessage",
"value": { "expr": "lit", "value": "Failed to load users" }
}
]
}
]
}POST Request Example
{
"name": "createTodo",
"steps": [
{
"do": "fetch",
"url": { "expr": "lit", "value": "https://api.example.com/todos" },
"method": "POST",
"body": {
"expr": "state",
"name": "newTodo"
},
"onSuccess": [
{
"do": "update",
"target": "todos",
"operation": "push",
"value": { "expr": "param", "name": "result" }
},
{
"do": "set",
"target": "newTodo",
"value": { "expr": "lit", "value": { "title": "", "completed": false } }
}
]
}
]
}Tip
In onSuccess handlers, the result parameter contains the parsed JSON response. In onError handlers, the error parameter contains error information.
Combining Steps
Actions can contain multiple steps that execute sequentially:
{
"name": "submitForm",
"steps": [
{
"do": "set",
"target": "isSubmitting",
"value": { "expr": "lit", "value": true }
},
{
"do": "set",
"target": "error",
"value": { "expr": "lit", "value": null }
},
{
"do": "fetch",
"url": { "expr": "lit", "value": "/api/submit" },
"method": "POST",
"body": { "expr": "state", "name": "formData" },
"onSuccess": [
{
"do": "set",
"target": "isSubmitting",
"value": { "expr": "lit", "value": false }
},
{
"do": "set",
"target": "submitted",
"value": { "expr": "lit", "value": true }
}
],
"onError": [
{
"do": "set",
"target": "isSubmitting",
"value": { "expr": "lit", "value": false }
},
{
"do": "set",
"target": "error",
"value": { "expr": "param", "name": "error" }
}
]
}
]
}Navigate Step
Navigates to a different URL or page.
{
"do": "navigate",
"url": { "expr": "lit", "value": "/dashboard" }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "navigate" | Yes | - | Step type identifier. |
| url | Expression | Yes | - | Expression that evaluates to the target URL. |
| target | "_self" | "_blank" | No | "_self" | Navigation target (same tab or new tab). |
| replace | boolean | No | false | Use history.replaceState instead of pushState. |
Examples
// Navigate to a page
{ "do": "navigate", "url": { "expr": "lit", "value": "/about" } }
// Open in new tab
{
"do": "navigate",
"url": { "expr": "lit", "value": "https://example.com" },
"target": "_blank"
}
// Dynamic navigation
{
"do": "navigate",
"url": {
"expr": "bin",
"op": "+",
"left": { "expr": "lit", "value": "/posts/" },
"right": { "expr": "var", "name": "postId" }
}
}Storage Step
Interacts with browser localStorage or sessionStorage.
{
"do": "storage",
"operation": "get",
"key": { "expr": "lit", "value": "theme" },
"storage": "local",
"result": "savedTheme"
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "storage" | Yes | - | Step type identifier. |
| operation | "get" | "set" | "remove" | Yes | - | Storage operation to perform. |
| key | Expression | Yes | - | Expression that evaluates to the storage key. |
| storage | "local" | "session" | Yes | - | Storage type (localStorage or sessionStorage). |
| value | Expression | No | - | Value to store (required for 'set' operation). |
| result | string | No | - | Variable name to store the retrieved value (for 'get' operation). |
| onSuccess | ActionStep[] | No | - | Steps to execute on success. |
| onError | ActionStep[] | No | - | Steps to execute on error. |
Storage Operations
| Operation | Description | Requires Value | Requires Result |
|---|---|---|---|
get | Retrieve value from storage | No | Yes (recommended) |
set | Store value in storage | Yes | No |
remove | Remove key from storage | No | No |
Examples
// Save theme preference
{
"do": "storage",
"operation": "set",
"key": { "expr": "lit", "value": "theme" },
"value": { "expr": "state", "name": "theme" },
"storage": "local"
}
// Load theme on mount
{
"do": "storage",
"operation": "get",
"key": { "expr": "lit", "value": "theme" },
"storage": "local",
"result": "savedTheme",
"onSuccess": [
{
"do": "if",
"condition": { "expr": "var", "name": "savedTheme" },
"then": [
{ "do": "set", "target": "theme", "value": { "expr": "var", "name": "savedTheme" } }
]
}
]
}
// Clear user session
{
"do": "storage",
"operation": "remove",
"key": { "expr": "lit", "value": "userToken" },
"storage": "session"
}DOM Step
Performs direct DOM manipulation operations.
{
"do": "dom",
"operation": "addClass",
"selector": { "expr": "lit", "value": "html" },
"value": { "expr": "lit", "value": "dark" }
}| Name | Type | Required | Default | Description |
|---|
DOM Operations
| Operation | Description | Requires Value | Requires Attribute |
|---|---|---|---|
addClass | Add a CSS class | Yes | No |
removeClass | Remove a CSS class | Yes | No |
toggleClass | Toggle a CSS class | Yes | No |
setAttribute | Set an attribute | Yes | Yes |
removeAttribute | Remove an attribute | No | Yes |
Examples
// Add dark mode class to html element
{
"do": "dom",
"operation": "addClass",
"selector": { "expr": "lit", "value": "html" },
"value": { "expr": "lit", "value": "dark" }
}
// Remove light mode class
{
"do": "dom",
"operation": "removeClass",
"selector": { "expr": "lit", "value": "html" },
"value": { "expr": "lit", "value": "light" }
}
// Set data attribute
{
"do": "dom",
"operation": "setAttribute",
"selector": { "expr": "lit", "value": "body" },
"attribute": "data-theme",
"value": { "expr": "state", "name": "theme" }
}Note
The dom step is primarily used for theme switching and other cases where you need to modify elements outside the Constela-managed view tree.
Import Step
Dynamically imports an external JavaScript module at runtime.
{
"do": "import",
"module": "monaco-editor",
"result": "monaco",
"onSuccess": [
{ "do": "set", "target": "editorReady", "value": { "expr": "lit", "value": true } }
]
}| Name | Type | Required | Default | Description |
|---|
Example: Loading Monaco Editor
{
"externalImports": {
"monaco-editor": "https://cdn.jsdelivr.net/npm/monaco-editor@0.52.0/+esm"
},
"lifecycle": {
"onMount": "initEditor"
},
"actions": [{
"name": "initEditor",
"steps": [{
"do": "import",
"module": "monaco-editor",
"result": "monaco",
"onSuccess": [{
"do": "call",
"target": { "expr": "var", "name": "monaco", "path": "editor.create" },
"args": [
{ "expr": "ref", "name": "editorContainer" },
{ "expr": "lit", "value": { "language": "json", "theme": "vs-dark" } }
],
"result": "editorInstance"
}]
}]
}]
}Call Step
Calls a JavaScript function on an imported module or DOM element.
{
"do": "call",
"target": { "expr": "var", "name": "monaco", "path": "editor.create" },
"args": [
{ "expr": "ref", "name": "container" },
{ "expr": "lit", "value": { "language": "json" } }
],
"result": "editor"
}| Name | Type | Required | Default | Description |
|---|
Examples
// Call a method on an imported library
{
"do": "call",
"target": { "expr": "state", "name": "editor", "path": "getValue" },
"result": "editorValue"
}
// Call with arguments
{
"do": "call",
"target": { "expr": "state", "name": "chart", "path": "update" },
"args": [{ "expr": "state", "name": "chartData" }]
}Subscribe Step
Subscribes to events from an external JavaScript object.
{
"do": "subscribe",
"target": { "expr": "state", "name": "editor" },
"event": "onDidChangeModelContent",
"action": "onCodeChange"
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "subscribe" | Yes | - | Step type identifier. |
| target | Expression | Yes | - | Expression that evaluates to an object with event methods. |
| event | string | Yes | - | Event method name to subscribe to. |
| action | string | Yes | - | Action name to execute when the event fires. |
Example: Monaco Editor Change Events
{
"actions": [
{
"name": "setupEditor",
"steps": [
{
"do": "subscribe",
"target": { "expr": "state", "name": "editor" },
"event": "onDidChangeModelContent",
"action": "onCodeChange"
}
]
},
{
"name": "onCodeChange",
"steps": [
{
"do": "call",
"target": { "expr": "state", "name": "editor", "path": "getValue" },
"result": "currentCode",
"onSuccess": [
{ "do": "set", "target": "code", "value": { "expr": "var", "name": "currentCode" } }
]
}
]
}
]
}Dispose Step
Manually disposes of a resource (cleanup subscriptions, destroy editors, etc.).
{
"do": "dispose",
"target": { "expr": "state", "name": "editor" }
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "dispose" | Yes | - | Step type identifier. |
| target | Expression | Yes | - | Expression that evaluates to an object with a dispose() method. |
Example: Cleanup on Unmount
{
"lifecycle": {
"onUnmount": "cleanup"
},
"actions": [{
"name": "cleanup",
"steps": [
{ "do": "dispose", "target": { "expr": "state", "name": "subscription" } },
{ "do": "dispose", "target": { "expr": "state", "name": "editor" } }
]
}]
}Tip
Always dispose of external resources in the onUnmount lifecycle hook to prevent memory leaks.
If Step
Conditionally executes steps based on a condition.
{
"do": "if",
"condition": { "expr": "state", "name": "isLoggedIn" },
"then": [
{ "do": "navigate", "url": { "expr": "lit", "value": "/dashboard" } }
],
"else": [
{ "do": "navigate", "url": { "expr": "lit", "value": "/login" } }
]
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| do | "if" | Yes | - | Step type identifier. |
| condition | Expression | Yes | - | Expression that evaluates to a boolean. |
| then | ActionStep[] | Yes | - | Steps to execute when condition is true. |
| else | ActionStep[] | No | - | Steps to execute when condition is false. |
Examples
// Conditional theme application
{
"do": "if",
"condition": { "expr": "var", "name": "savedTheme" },
"then": [
{ "do": "set", "target": "theme", "value": { "expr": "var", "name": "savedTheme" } },
{
"do": "dom",
"operation": "addClass",
"selector": { "expr": "lit", "value": "html" },
"value": { "expr": "var", "name": "savedTheme" }
}
]
}
// Validation before submit
{
"do": "if",
"condition": {
"expr": "bin",
"op": "!=",
"left": { "expr": "state", "name": "email" },
"right": { "expr": "lit", "value": "" }
},
"then": [
{ "do": "fetch", "url": { "expr": "lit", "value": "/api/submit" }, "method": "POST" }
],
"else": [
{ "do": "set", "target": "error", "value": { "expr": "lit", "value": "Email is required" } }
]
}