Unify Logo Footer.svg
Unify Applications
Logo
Stepper V2

Stepper V2

Logo

7 mins READ

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

FeatureStepper V1Stepper V2
SubstepsNot supportedEach step can have a substeps array
Status modelpending / active / completedpending / active / completed / error / skipped
Validation gateNot availableonValidate event fires before each advance; return false to block
Skip supportNot availableallowSkip prop; skip Control Block operation
Controlled modeUncontrolled onlyFully 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:

FieldTypeRequiredDescription
idstringRequired (required)Unique identifier for this step. Referenced in Control Block operations and event payloads.
labelstringRequired (required)Display text for the step node. Rendered next to the step indicator circle/icon.
iconstring (icon key)Optional (optional)Icon rendered inside the step indicator circle. Overrides the default number/checkmark indicator when set.
statusenumOptional (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.
substepsarray of substep objectsOptional (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.
descriptionstringOptional (optional)Secondary text rendered below the step label in smaller, muted style. Use for progress hints or status messages.

Block-Level Properties

PropertyTypeDefaultDescription
stepsarray (bindable)Sample stepsThe step definitions. Bind to a static array, page variable, or data source.
orientationenum: horizontal | verticalhorizontalHorizontal 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.
activeStepnumber (bindable)0Zero-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).
allowSkipbooleanfalseShow a "Skip" link below the active step so users can advance without completing it. The skipped step receives status: skipped.
validateOnNextbooleanfalseWhen 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.
clickablebooleanfalseAllow 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.
onStepChangeaction chainFires after the active step changes. Payload: { fromIndex, toIndex, fromId, toId, direction: "forward" | "back" | "jump" }.
onValidateaction chainFires when validateOnNext = true and the user clicks Next. Must call resolve(boolean) to allow or block the advance. Payload: { currentStep, currentIndex }.
onCompleteaction chainFires 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

  1. Define the steps array: Set steps to a static array with three steps: Applicant Info (substeps: Personal Details, Contact Details), Documents (substeps: ID Upload, Proof of Address), and Review & Submit.

  2. 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.

  3. 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.

  4. 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.

PageRelationship
Timeline & Stepper V1The original Stepper — simpler, no substeps or validation gate
Tabs & Stepped ContainerStepped Container renders each step's form content; pair with StepperV2 for the navigation indicator
StoriesTimed sequential reveal — use when steps don't need forms or validation