Programmatic Navigation
The Navigate action lets widgets trigger navigation on events — button clicks, form submits, workflow completions, conditionals — with full support for route params, query strings, history control, and chaining.
Navigate Action Basics
Any widget that fires an action (buttons, links, form submissions, workflow result handlers) can use a navigate action to move the user to another AppPage or external URL:
// Navigate action on a Button widget
{
"type": "navigate",
"target": "/leads/{{ row.id }}", // Route path or pageId
"params": { // Additional query string params
"returnUrl": "{{ route.currentPath }}"
},
"mode": "push" // "push" | "replace" | "new-tab"
}
Target Options
| Target format | Example | Behavior |
|---|---|---|
| Route path | /leads/{{ row.id }} | Navigate to the AppPage whose route matches the path |
| pageId | lead-detail | Navigate to the AppPage with that pageId (route params filled from current context) |
| Absolute URL | https://example.com | Navigate to an external URL (opens in new tab by default) |
| Relative path | ../list | Navigate relative to the current route base |
Navigation Mode: push vs. replace
The mode property controls browser history behavior:
// push (default) — adds a new entry to browser history
// Back button returns to the current page
{
"type": "navigate",
"target": "/leads/{{ row.id }}",
"mode": "push"
}
// replace — replaces the current history entry
// Back button skips the current page (useful for wizard steps)
{
"type": "navigate",
"target": "/leads/{{ row.id }}/step-2",
"mode": "replace"
}
// new-tab — opens the target in a new browser tab
{
"type": "navigate",
"target": "/leads/{{ row.id }}",
"mode": "new-tab"
}
Navigating with Parameters
Pass additional query parameters alongside the route:
// Navigate to edit page with a return URL
{
"type": "navigate",
"target": "/leads/{{ row.id }}/edit",
"params": {
"returnUrl": "/leads",
"source": "list-view"
}
}
// Results in: /app/acme/crm/leads/lead-456/edit?returnUrl=%2Fleads&source=list-view
// Reading the param on the edit page:
{{ route.returnUrl }} → "/leads"
{{ route.source }} → "list-view"
Conditional Navigation
Use the conditional action type to choose between navigation targets based on a runtime expression:
// Navigate to different pages based on lead status
{
"type": "conditional",
"condition": "{{ row.status === 'qualified' }}",
"then": {
"type": "navigate",
"target": "/opportunities/new",
"params": { "sourceLeadId": "{{ row.id }}" }
},
"else": {
"type": "navigate",
"target": "/leads/{{ row.id }}/nurture"
}
}
Navigate After Workflow Completion
Chain a navigate action after a workflow succeeds or fails:
// Button that triggers a workflow, then navigates on success
{
"type": "chain",
"actions": [
{
"type": "trigger-workflow",
"workflowId": "CreateLead",
"inputMapping": {
"name": "{{ variables.leadName }}",
"email": "{{ variables.leadEmail }}"
},
"outputVariable": "createResult"
},
{
"type": "conditional",
"condition": "{{ workflowOutput.createResult.success }}",
"then": {
"type": "navigate",
"target": "/leads/{{ workflowOutput.createResult.leadId }}",
"mode": "replace" // replace so back button goes to list, not the form
},
"else": {
"type": "set-variable",
"variable": "errorMessage",
"value": "{{ workflowOutput.createResult.error }}"
}
}
]
}
Navigate After Form Submit
// Form widget — navigate on successful submission
{
"widgetId": "lead-form",
"type": "Form",
"config": {
"submitAction": {
"type": "chain",
"actions": [
{ "type": "submit-form", "formId": "lead-form" },
{
"type": "navigate",
"target": "{{ route.returnUrl ?? '/leads' }}",
"mode": "replace"
}
]
}
}
}
Navigating to Home
// Navigate to app home page (isHome: true AppPage)
{
"type": "navigate",
"target": "/"
}
// Navigate to a specific named page using pageId shorthand
{
"type": "navigate",
"target": "dashboard" // resolves to the "dashboard" AppPage's route
}
Using returnUrl Pattern
A common UX pattern passes a returnUrl so the user is sent back to where they came from after completing an action:
// Step 1 — on the list page, navigate to edit and pass returnUrl
{
"type": "navigate",
"target": "/leads/{{ row.id }}/edit",
"params": { "returnUrl": "/leads" }
}
// Step 2 — on the edit page, use returnUrl to go back after save
{
"type": "navigate",
"target": "{{ route.returnUrl ?? '/leads' }}",
"mode": "replace"
}
// Using the nullish coalescing operator ensures a safe fallback
// if returnUrl is not present in the URL
"mode": "replace" on wizard-step transitions and post-save redirects so the browser back button skips intermediate steps and returns users to the logical origin page.