Overview
Stepper V2 (StepperV2) builds on the original Stepper with three major additions: substep nesting (each top-level step can have its own ordered list of sub-tasks), a richer status model (pending, active, completed, error, skipped), and validation gates that fire an onValidate event before the stepper advances so your action chain can reject the transition if form fields are invalid or required conditions are not met.
Differences from Stepper V1
| Feature | Stepper V1 | Stepper V2 |
| Substeps | Not supported | Each step can have a substeps array |
| Status model | pending / active / completed | pending / active / completed / error / skipped |
| Validation gate | Not available | onValidate event fires before each advance; return false to block |
| Skip support | Not available | allowSkip prop; skip Control Block operation |
| Controlled mode | Uncontrolled only | Fully controlled via activeStep binding |
Steps Array
Each item in the steps array is one top-level step node. Steps may contain a nested substeps array:
| Field | Type | Required | Description |
| id | string | Required (required) | Unique identifier for this step. Referenced in Control Block operations and event payloads. |
| label | string | Required (required) | Display text for the step node. Rendered next to the step indicator circle/icon. |
| icon | string (icon key) | Optional (optional) | Icon rendered inside the step indicator circle. Overrides the default number/checkmark indicator when set. |
| status | enum | Optional (optional) | Step state: pending (not yet reached), active (current), completed (done, checkmark), error (failed validation, red indicator), skipped (skipped by user or logic, grey strikethrough). When unset, the stepper computes status from activeStep. |
| substeps | array of substep objects | Optional (optional) | Nested sub-task list shown indented under the parent step. Each substep: { id, label, status }. Substeps use the same status enum. They are visual-only unless you bind to them in your action chain logic. |
| description | string | Optional (optional) | Secondary text rendered below the step label in smaller, muted style. Use for progress hints or status messages. |
Block-Level Properties
| Property | Type | Default | Description |
| steps | array (bindable) | Sample steps | The step definitions. Bind to a static array, page variable, or data source. |
| orientation | enum: horizontal | vertical | horizontal | Horizontal lays steps left-to-right along a connecting line. Vertical stacks steps top-to-bottom — preferred when steps have substeps or long labels, or when the page layout is narrow. |
| activeStep | number (bindable) | 0 | Zero-based index of the currently active step. Bind to a page variable to control the stepper externally. When unset or unbound, the stepper manages its own active state internally (uncontrolled mode). |
| allowSkip | boolean | false | Show a "Skip" link below the active step so users can advance without completing it. The skipped step receives status: skipped. |
| validateOnNext | boolean | false | When true, the stepper fires the onValidate event before advancing to the next step. The advance is blocked until the action chain calls resolve(true). If the chain calls resolve(false), the active step's status is set to error and the stepper stays on the current step. |
| clickable | boolean | false | Allow the user to click completed or pending steps to navigate to them directly — bypassing sequential order. Only appropriate for flows where skipping ahead is acceptable. |
| onStepChange | action chain | — | Fires after the active step changes. Payload: { fromIndex, toIndex, fromId, toId, direction: "forward" | "back" | "jump" }. |
| onValidate | action chain | — | Fires when validateOnNext = true and the user clicks Next. Must call resolve(boolean) to allow or block the advance. Payload: { currentStep, currentIndex }. |
| onComplete | action chain | — | Fires when the user advances past the last step. Use to submit a form, navigate, or show a success message. |
Validation Gate Pattern
The validation gate fires onValidate when the user clicks Next. Your action chain runs synchronously, performs its checks, and calls the built-in resolve function with true (advance) or false (block). This pattern allows inline form validation without custom button wiring.
Example: Blocking advance on empty required fields
In the onValidate action chain: add a Condition action that checks {{ formEmail.value !== "" && formName.value !== "" }}. In the True branch: add a Resolve action with value = true. In the False branch: add a Show Notification action (error variant, "Please fill in all required fields"), then a Resolve action with value = false. The stepper stays on the current step and the active step indicator turns red.
Working with Substeps
Substeps render as a vertically indented list under their parent step. They are visible only when the parent step is active or completed. You manage substep statuses explicitly — the stepper does not auto-advance substeps. Use page variables or data source fields to track which substeps are done and bind them to the status field of each substep object.
Tip: Maintain a substepStatuses map variable keyed by substep ID. When a sub-task completes (e.g., a file uploads, an API call succeeds), use a Set Variable action to update the relevant substep's status to "completed". Bind the steps array to an expression that merges the static step definitions with the dynamic status map.
Controlled Mode
When you bind activeStep to a page variable, the stepper becomes controlled: it always shows the step at the bound index, and advancing Next/Back updates the variable rather than its own internal state. Controlled mode is required when the stepper is rendered inside a Repeatable or when the active step must be persisted across page navigations (by saving the variable to app state).
Setting Up a Multi-Stage Review Form
Define the steps array: Set
stepsto a static array with three steps: Applicant Info (substeps: Personal Details, Contact Details), Documents (substeps: ID Upload, Proof of Address), and Review & Submit.Set orientation and controls: Set Orientation = vertical (substeps render better vertically). Set Validate On Next = true. Create a page variable
activeStep(number, default 0) and bind it to the Active Step property.Wire validation: In onValidate, check each step's required fields based on
{{ event.currentIndex }}. Step 0 validates personal fields; step 1 validates document uploads. Resolve true/false accordingly.Wire onComplete: In onComplete, call an API action to submit the collected form data. On success, show a success notification and navigate to a confirmation page.
Frequently Asked Questions
Can I use Stepper V2 alongside a Stepped Container?
Yes, and this is the recommended pattern for multi-step forms. Place the StepperV2 block above (or beside) a Stepped Container. Bind the Stepped Container's activeStep to the same page variable as the StepperV2's activeStep. The Stepper V2 acts as the visual navigation indicator while the Stepped Container renders the step-specific form content. When the user advances via the Stepper's built-in Next button, both blocks update in sync.
How do I mark a step as error after an API call fails?
In the onValidate action chain, add an API Call action. In the API call's onError handler, call Resolve with false. This signals the validation gate to set the step's status to error and block the advance. You can also set a step's status to error manually by updating the status field in the bound steps array variable from any action chain — not just from validation.
What is the difference between allowSkip and clickable?
allowSkip adds a "Skip" link below the currently active step, setting its status to skipped and advancing forward. clickable lets users click on any completed or pending step node to jump directly to it. Use allowSkip for flows where some steps are optional but must still be visited in order. Use clickable for review-style flows where users are expected to navigate non-linearly to edit earlier steps.
Related Pages
| Page | Relationship |
| Timeline & Stepper V1 | The original Stepper — simpler, no substeps or validation gate |
| Tabs & Stepped Container | Stepped Container renders each step's form content; pair with StepperV2 for the navigation indicator |
| Stories | Timed sequential reveal — use when steps don't need forms or validation |