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.
Prerequisites
- 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
Add the Wizard block to your screen
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.
Create content containers for each step
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
1/* If widget conditions */2CurrentStep = 1 /* shows Personal Info container */3CurrentStep = 2 /* shows Employment container */4CurrentStep = 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.
Define Local Variables to hold all form data
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.
Add Next/Back navigation buttons with step validation
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.
1/* Step 1 Next validation */2If: Length(Trim(FullName)) = 034/* Assign for invalid */5FullNameInput.Valid = False6FullNameInput.ValidationMessage = "Full name is required"78/* Advance step */9CurrentStep = 21011/* Back button Assign */12CurrentStep = CurrentStep - 1Expected 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.
Build the Review step and final Submit action
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.
1/* FormatDate expression for review display */2FormatDate(StartDate, "MMMM d, yyyy")34/* CreateEmployee input mapping (Server Action) */5Employee.FullName = FullName6Employee.Email = Email7Employee.PhoneNumber = PhoneNumber8Employee.DepartmentId = DepartmentId9Employee.StartDate = StartDateExpected 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 working example
1/* Screen: EmployeeOnboarding23 Local Variables:4 CurrentStep : Integer = 15 FullName : Text = ""6 Email : Email = ""7 PhoneNumber : Text = ""8 DepartmentId : Department Identifier = NullIdentifier()9 StartDate : Date = NullDate()1011 Widget tree (simplified):12 Wizard (ActiveStep = CurrentStep)13 WizardItem (Label='Personal Info', Step=1)14 WizardItem (Label='Employment', Step=2)15 WizardItem (Label='Review', Step=3)16 If (CurrentStep = 1)17 Container Step118 Form119 Input FullName (Variable=FullName, Mandatory=True)20 Input Email (Variable=Email, Mandatory=True)21 Input PhoneNumber (Variable=PhoneNumber)22 Button 'Next' (OnClick=Step1Next)23 If (CurrentStep = 2)24 Container Step225 Form226 Dropdown Department (Variable=DepartmentId)27 Input StartDate (Variable=StartDate, Mandatory=True)28 Button 'Back' (Assign CurrentStep=1)29 Button 'Next' (OnClick=Step2Next)30 If (CurrentStep = 3)31 Container Step332 Expression FullName33 Expression Email34 Expression FormatDate(StartDate,"MMMM d, yyyy")35 Button 'Back' (Assign CurrentStep=2)36 Button 'Submit' (OnClick=SubmitEmployee)37*/3839/* Client Action: Step1Next */40Start41 --> If: Length(Trim(FullName)) = 042 [True] --> Assign FullNameInput.Valid=False, FullNameInput.ValidationMessage="Full name required" --> End43 --> If: not Form1.Valid44 [True] --> End45 --> Assign: CurrentStep = 246 --> End4748/* Client Action: Step2Next */49Start50 --> If: DepartmentId = NullIdentifier()51 [True] --> Message "Please select a department" (Warning) --> End52 --> If: StartDate = NullDate()53 [True] --> Message "Please enter a start date" (Warning) --> End54 --> Assign: CurrentStep = 355 --> End5657/* Client Action: SubmitEmployee */58Start59 --> CreateEmployee: FullName, Email, PhoneNumber, DepartmentId, StartDate60 --> Message: "Employee registered!" (Success)61 --> Destination: EmployeeListScreen62 --> EndCommon mistakes
Why it's a problem: Using separate screens for each wizard step instead of If widgets on one screen
How to avoid: 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.
Why it's a problem: Checking Form.Valid before setting individual input validation states
How to avoid: 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.
Why it's a problem: Saving data to the database at every step
How to avoid: 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.
Why it's a problem: Not resetting CurrentStep to 1 when the user submits and the screen is reused
How to avoid: 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.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a 3-step wizard form in OutSystems 11 Reactive Web. The steps are: Personal Info (FullName, Email), Employment Details (DepartmentId dropdown, StartDate), and Review & Submit. Explain how to use the OutSystems UI Wizard pattern, manage CurrentStep with a Local Variable, show/hide step containers with If widgets, validate per-step before advancing, and save data only on the final Submit using a Server Action.
Create a 3-step wizard form in OutSystems on the EmployeeOnboarding screen. Step 1: Personal Info with FullName (Text, mandatory, min 2 chars) and Email (Email type, mandatory). Step 2: Department dropdown (DepartmentId) and StartDate (Date). Step 3: Review display and Submit button. Use OutSystems UI Wizard block with ActiveStep = CurrentStep Local Variable. Validate Step 1 before advancing and call CreateEmployee Server Action only on final Submit.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation