Skip to content

Wizard

Status: ✅ Implemented

Guide sequential flows where each step depends on what was chosen in earlier steps.

An onboarding flow where step 3 depends on what was selected in step 1 cannot be safely collapsed into one large form — the user could advance without completing required choices, or see irrelevant fields. Jumping between pages with no intermediate validation breaks the flow.

Extend Wizard and declare one field per step; each field’s type must implement WizardStep. Mateu renders the current step’s form, a progress indicator, and navigation buttons automatically. The progress indicator is a bar by default; annotate the wizard with @WizardProgress(WizardProgressStyle.STEPS) to show connected step bullets instead (one numbered dot per applicable step with done/current/upcoming states — skipped branching steps are excluded, and every dot shows done on the result screen), or @WizardProgress(WizardProgressStyle.RAIL) for the guided-process lateral rail: the step form on the left and a sticky right-hand band with a big current | total counter over the vertical step list (demo: /branching-wizard). State set in any step — by the user or by an action — survives navigation in both directions and reaches the completion action, so steps can freely read what earlier (or later) steps produced.

// Each step is a plain class or record implementing WizardStep
public class AccountTypeStep implements WizardStep {
@NotNull
AccountType accountType;
}
public class CompanyDetailsStep implements WizardStep {
String companyName;
String vatNumber;
}
// Result step — read-only screen shown after completion
public class OnboardingResult implements WizardStep {
@PlainText String summary = "Account created successfully.";
}
// The wizard class
@UI("/onboarding")
public class OnboardingWizard extends Wizard {
AccountTypeStep step1 = new AccountTypeStep();
CompanyDetailsStep step2 = new CompanyDetailsStep();
OnboardingResult result; // null → auto-instantiated after @WizardCompletionAction
@WizardCompletionAction
@Action(validationRequired = true)
Object finish() {
accountService.create(step1, step2);
result = new OnboardingResult(); // optional: set explicitly for custom data
return null;
}
}
Step positionBehaviour
Any intermediate stepShows Next → (and ← Back after step 1). Validation runs on Next →.
Penultimate stepShows the @WizardCompletionAction button instead of Next →.
Last stepRead-only result screen. No navigation buttons. Progress bar shows 100 %.

The last step is instantiated automatically with its default field values if it is null when @WizardCompletionAction returns — or the wizard can set it explicitly inside the completion method.

The wizard title is derived in order: @Title annotation → TitleSupplier.title() → class name.

Registration wizard — step 1 with progress bar and Next button

Override stepApplies(String stepFieldName) to skip steps based on the answers so far. A skipped step is jumped over in both directions (Next and Back), excluded from the progress bar, and left out of the accordion / previous-answers recap in the other layout modes. The result step always applies.

public class SignupWizard extends Wizard {
AccountTypeStep account = new AccountTypeStep(); // asks PERSONAL / COMPANY
CompanyDetailsStep company = new CompanyDetailsStep();
PlanStep plan = new PlanStep();
ResultStep result;
@Override
protected boolean stepApplies(String stepFieldName) {
if ("company".equals(stepFieldName)) {
return account.accountType == AccountType.COMPANY; // skip for personal accounts
}
return true;
}
@WizardCompletionAction
@Action(validationRequired = true)
Object finish() { /* … */ return null; }
}

stepApplies is evaluated on every render and navigation, so it can depend on values captured by any earlier step. When the skipped step was the penultimate one, the completion button moves to the last applicable step automatically.

By default a wizard shows one step at a time. Annotate the class with @WizardLayout(...) to change how it’s laid out:

@WizardLayout(WizardLayoutMode.ACCUMULATIVE)
public class OnboardingWizard extends Wizard { … }
ModeBehaviour
STEPS (default)Only the current step is shown, one at a time.
ACCUMULATIVEThe current step is editable, with a single compact “Previous answers” recap card above it — every completed step’s values listed as dense label: value lines, grouped by step — so the user always sees what has been collected so far without it dominating the screen.
ACCORDIONEvery step is a collapsible panel: the current one is open and editable, completed ones are collapsed (expand to review), upcoming ones are disabled. As you advance, the previous panel collapses and the next opens.
Accumulative wizard — completed steps recapped above the current one
ACCUMULATIVE
Accordion wizard — one collapsible panel per step
ACCORDION

Both non-default modes render previously entered data read-only; steps should use distinct field names (the wizard state is a single flat map across steps).

Account setup ← getTitle()
[●────────────────────] Step 1 / 3 ← progress bar
Account type: ○ Personal ● Business
[Next →]
Account setup
[────●────────────────] Step 2 / 3
Company name: ___________
VAT number: ___________
[← Back] [Create account] ← @WizardCompletionAction
Account setup
[──────────────────────●] Done
✓ Account created successfully.
← no navigation buttons

What the Redwood guided-process template exposes, and what Mateu gives you for it. The gaps here are the transactional ones — drafts, resuming and skipping — which is the largest coherent piece of Redwood surface Mateu does not cover. The canonical page-header elements shared by every template are documented once in Page templates.

Legend: ✅ supported · 🟡 partial · — not supported · ⚪ deliberately out of scope

Redwood prop / slotMateu
processTitle / processSubtitlegetTitle() / @Title
steps[] / currentStepstep fields; numberOfSteps(), currentStepNumber(), getStep()
displayOptions.checklistDisplay: current | all@WizardProgress(BAR | STEPS | RAIL) picks the progress presentation
Conditional stepsstepApplies(stepFieldName) — branching
Completion stepSlot completionStep@WizardCompletionAction + the done state
Validation before advancingvalidations() runs before the step advances
avatar + displayOptions.avatarPageDto.avatar/icon on the canonical header🟡
primaryAction.availableFromStep (enable the finish action from step N on)
resumeStepId (resume where the user left off)
displayOptions {save, saveAndClose} + spSave / spSaveAndClose (drafts)— there is no draft concept; the wizard state lives in the page state for the duration of the flow
spSkip {skippedStepId} (user-initiated skip)stepApplies skips a step by rule, but the user cannot skip one🟡
spBeforeNext / spBeforeStepNavigate (cancelable hooks)validation runs, but there is no declarative cancelable hook — do it inside the action🟡
completionStatus / continueWorkingStatusthe done state is terminal🟡
displayOptions.overviewAnimation
displayOptions.density: standard | compact@Compact, set on the view rather than as a template option🟡
Slot announcement (aria-live)live regions are installed client-side for a11y, but the backend cannot declare announcement content🟡

The related step-by-step-page template (a full-screen linear process) adds timer {startTime, timeInterval} for timed processes and spFinishLater; neither is built. For a wizard inside a drawer, see Guided Process Drawer.

  • Progressive complexity — each step shows only what is needed at that moment
  • Recoverability — validation fires before advancing, not at the end
  • Workflow over screens — the wizard models a task, not an entity