# Building Multi-Step Wizard Forms in OutSystems

- Tool: OutSystems
- Difficulty: Intermediate
- Time required: 35-50 min
- Compatibility: OutSystems 11 and ODC
- Last updated: March 2026

## TL;DR

Use the OutSystems UI Wizard pattern (Navigation category in the Toolbox) with a Local Variable tracking the current step number. Show each step's content with If widgets bound to CurrentStep = N. Validate fields before calling the Wizard's Next action by checking individual inputs and Form.Valid. Save data only on the final step.

## Multi-Step Wizard Forms in OutSystems

Long forms overwhelm users. The Wizard pattern breaks them into manageable steps with a visual progress indicator. OutSystems UI includes a Wizard block in its Navigation category that handles the step indicator visuals; your job is to wire up the step logic, per-step validation, and state management. This tutorial builds a 3-step employee onboarding form: Personal Info → Employment Details → Review & Submit.

## Before you start

- OutSystems UI library referenced in your module (it is included by default in Reactive Web Apps)
- An Employee entity with fields matching the form data you want to collect
- Familiarity with Local Variables and Client Actions in Service Studio

## Step-by-step guide

### 1. Add the Wizard block to your screen

In the Interface tab, open your screen. From the Toolbox (left panel), search for 'Wizard' under the Navigation category. Drag the Wizard block onto the canvas. The Wizard block renders a horizontal step progress bar at the top. By default it renders three WizardItem children — each WizardItem represents one step bubble in the indicator.

Select each WizardItem → in Properties Panel:
- WizardItem 1: Label = 'Personal Info', Step = 1
- WizardItem 2: Label = 'Employment', Step = 2
- WizardItem 3: Label = 'Review', Step = 3

Create a Local Variable on the screen: right-click screen name → Add Local Variable → Name = 'CurrentStep', Type = Integer, Default Value = 1.

**Expected result:** The screen shows a 3-step progress bar with step labels. Step 1 is highlighted because CurrentStep = 1.

### 2. Create content containers for each step

Below the Wizard block, add three Container widgets (one per step). Wrap each in an If widget to show/hide based on CurrentStep.

In the Widget Tree: add three If widgets stacked vertically.
- If 1: Condition = CurrentStep = 1 → True branch contains Container for Personal Info fields
- If 2: Condition = CurrentStep = 2 → True branch contains Container for Employment fields
- If 3: Condition = CurrentStep = 3 → True branch contains Container for Review & Submit

Add appropriate Input widgets inside each Container:
- Step 1: FullName (Text), Email (Email), PhoneNumber (Text)
- Step 2: Department (Dropdown), StartDate (Date)
- Step 3: Summary labels showing collected data + Submit button

```
/* If widget conditions */
CurrentStep = 1   /* shows Personal Info container */
CurrentStep = 2   /* shows Employment container */
CurrentStep = 3   /* shows Review container */
```

**Expected result:** Only the Step 1 container is visible at startup. Steps 2 and 3 are hidden. All input widgets in Step 1 are visible and editable.

### 3. Define Local Variables to hold all form data

Data must persist across steps because each step's content is hidden (not destroyed) when CurrentStep changes. Right-click the screen → Add Local Variable for each field:
- FullName: Text
- Email: Email
- PhoneNumber: Text
- DepartmentId: Department Identifier
- StartDate: Date

Bind each Input and Dropdown widget's Variable property to the matching Local Variable. Because Local Variables persist for the lifetime of the screen session, values entered in Step 1 remain available when you reach Step 3.

**Expected result:** All input widgets are bound. Entering data in Step 1 and advancing to Step 2 then back to Step 1 shows the entered values are preserved.

### 4. Add Next/Back navigation buttons with step validation

In each step's Container, add navigation buttons:
- Step 1: 'Next' button (no Back needed)
- Step 2: 'Back' and 'Next' buttons
- Step 3: 'Back' and 'Submit' buttons

For the Step 1 Next button → right-click → Add OnClick Client Action. Name it 'Step1Next'.

Action flow for Step1Next:
Start
  → If: Length(Trim(FullName)) = 0
       [True] → Assign: FullNameInput.Valid = False, FullNameInput.ValidationMessage = 'Full name is required' → End
  → If: not Form1.Valid
       [True] → End
  → Assign: CurrentStep = 2
  → End

For Back buttons, add a simple Assign: CurrentStep = CurrentStep - 1.

The WizardItem Step property must also update — since WizardItem.Step is bound to a fixed integer in the Properties Panel, and the Wizard block's ActiveStep is bound to CurrentStep, the visual indicator updates automatically.

```
/* Step 1 Next validation */
If: Length(Trim(FullName)) = 0

/* Assign for invalid */
FullNameInput.Valid = False
FullNameInput.ValidationMessage = "Full name is required"

/* Advance step */
CurrentStep = 2

/* Back button Assign */
CurrentStep = CurrentStep - 1
```

**Expected result:** Clicking Next on Step 1 with an empty FullName shows the validation error inline and does not advance. Filling in the data and clicking Next changes CurrentStep to 2 and shows Step 2 content.

### 5. Build the Review step and final Submit action

In the Step 3 Container, add Expression widgets that display the collected data as a summary for the user to review before submitting:
- Label: 'Name:', Expression: FullName
- Label: 'Email:', Expression: Email
- Label: 'Department:', Expression: GetDepartments.List.Current.Department.Name
- Label: 'Start Date:', Expression: FormatDate(StartDate, "MMMM d, yyyy")

Add a Submit button → right-click → Add OnClick Client Action. Action flow:
Start → CreateEmployee Server Action (pass all collected variables) → Message 'Employee registered successfully!' (Success) → Destination: EmployeeListScreen → End

The CreateEmployee Server Action (Logic tab → Server Actions) does the actual database write — only on final submission.

```
/* FormatDate expression for review display */
FormatDate(StartDate, "MMMM d, yyyy")

/* CreateEmployee input mapping (Server Action) */
Employee.FullName    = FullName
Employee.Email       = Email
Employee.PhoneNumber = PhoneNumber
Employee.DepartmentId = DepartmentId
Employee.StartDate   = StartDate
```

**Expected result:** Step 3 shows a clean summary of all entered data. Clicking Submit creates the employee record and navigates to the list screen with a success message.

## Complete code example

File: `WizardForm_screen_logic.txt`

```outsystems-pseudocode
/* Screen: EmployeeOnboarding

   Local Variables:
     CurrentStep  : Integer = 1
     FullName     : Text = ""
     Email        : Email = ""
     PhoneNumber  : Text = ""
     DepartmentId : Department Identifier = NullIdentifier()
     StartDate    : Date = NullDate()

   Widget tree (simplified):
     Wizard (ActiveStep = CurrentStep)
       WizardItem (Label='Personal Info', Step=1)
       WizardItem (Label='Employment',    Step=2)
       WizardItem (Label='Review',        Step=3)
     If (CurrentStep = 1)
       Container Step1
         Form1
           Input FullName      (Variable=FullName, Mandatory=True)
           Input Email         (Variable=Email, Mandatory=True)
           Input PhoneNumber   (Variable=PhoneNumber)
           Button 'Next'       (OnClick=Step1Next)
     If (CurrentStep = 2)
       Container Step2
         Form2
           Dropdown Department (Variable=DepartmentId)
           Input StartDate     (Variable=StartDate, Mandatory=True)
           Button 'Back'       (Assign CurrentStep=1)
           Button 'Next'       (OnClick=Step2Next)
     If (CurrentStep = 3)
       Container Step3
         Expression FullName
         Expression Email
         Expression FormatDate(StartDate,"MMMM d, yyyy")
         Button 'Back'   (Assign CurrentStep=2)
         Button 'Submit' (OnClick=SubmitEmployee)
*/

/* Client Action: Step1Next */
Start
  --> If: Length(Trim(FullName)) = 0
       [True] --> Assign FullNameInput.Valid=False, FullNameInput.ValidationMessage="Full name required" --> End
  --> If: not Form1.Valid
       [True] --> End
  --> Assign: CurrentStep = 2
  --> End

/* Client Action: Step2Next */
Start
  --> If: DepartmentId = NullIdentifier()
       [True] --> Message "Please select a department" (Warning) --> End
  --> If: StartDate = NullDate()
       [True] --> Message "Please enter a start date" (Warning) --> End
  --> Assign: CurrentStep = 3
  --> End

/* Client Action: SubmitEmployee */
Start
  --> CreateEmployee: FullName, Email, PhoneNumber, DepartmentId, StartDate
  --> Message: "Employee registered!" (Success)
  --> Destination: EmployeeListScreen
  --> End
```

## Common mistakes

- **Using separate screens for each wizard step instead of If widgets on one screen** — undefined Fix: Separate screens lose Local Variable state between navigation. Keep all wizard steps on one screen and use If widgets controlled by CurrentStep to show/hide content.
- **Checking Form.Valid before setting individual input validation states** — undefined Fix: Always set individual InputName.Valid = False (with ValidationMessage) before checking Form.Valid. The same pattern applies here as in single-step forms — see the outsystems-form-validation tutorial.
- **Saving data to the database at every step** — undefined Fix: Writing partial records at each step creates orphaned data if the user abandons the wizard. Collect all data in Local Variables across steps and write once in the final Submit action.
- **Not resetting CurrentStep to 1 when the user submits and the screen is reused** — undefined Fix: After a successful Submit and navigation, the screen's OnInitialize Client Action does not run again if the user returns via Back. Add Assign: CurrentStep = 1 at the top of the SubmitEmployee action before navigating away.

## Best practices

- Save data to the database only on the final Submit, not incrementally at each step, to prevent orphaned partial records.
- Bind the Wizard block's ActiveStep to your CurrentStep Local Variable so the visual indicator stays in sync automatically.
- Validate only the fields in the current step before advancing — do not run Step 2 validation when the user clicks Next on Step 1.
- Use a Structure to group all form variables into a single typed record when the wizard has more than four or five fields.
- Add a Cancel button that clears all Local Variables and navigates away, so users can start fresh without stale data.
- Test the Back navigation to confirm that previously entered data is still present when the user returns to an earlier step.

## Frequently asked questions

### Can I navigate directly to a specific step, such as from a URL parameter?

Yes. Add an Input Parameter to the screen (right-click screen → Add Input Parameter → 'InitialStep', Integer). In the screen's OnInitialize Client Action, assign CurrentStep = If(InitialStep > 0, InitialStep, 1). Pass the step number as a URL parameter when navigating to the screen.

### How do I save partial wizard progress so users can resume later?

Use a Client Variable (Data tab → Client Variables) to persist the form data across sessions. Assign form field values to the Client Variable at each step transition. On screen load, check if the Client Variable has data and pre-populate the form. Write to the database only on final Submit or when explicitly saving a draft.

### Is there a maximum number of wizard steps supported by the OutSystems UI Wizard pattern?

There is no hard limit. The Wizard block renders as many WizardItem children as you add. Practically, more than 6-7 steps becomes unwieldy on mobile screens — consider using a Sidebar or alternative navigation pattern for very long flows.

### How do I make the step indicator clickable so users can jump to any step?

By default WizardItems are non-interactive indicators. To make them clickable, add an OnClick event to each WizardItem and in the handler, assign CurrentStep to the target step number only if CurrentStep >= target (users can only go back, not skip ahead). Add validation if you want to block backward navigation too.

---

Source: https://www.rapidevelopers.com/outsystems-tutorial/outsystems-wizard-form
© RapidDev — https://www.rapidevelopers.com/outsystems-tutorial/outsystems-wizard-form
