Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<style>
#customized-wizard li[status="current"] > div {
font-weight: bold;
}

#customized-wizard li[disabled] > div {
color: var(--colorNeutralForeground2);
opacity: 0.4;
}
</style>

<FluentWizard @ref="@MyWizard"
Id="customized-wizard"
@bind-Value="@Value"
StepTitleHiddenWhen="@GridItemHidden.XsAndDown"
OnFinish="OnFinish"
Height="300px">
<Steps>
<FluentWizardStep OnChange="@OnStepChange">
<StepTemplate>
<div active="@context.Active">
Intro
</div>
</StepTemplate>
<ChildContent>
<h5>Introduction</h5>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ut nisi eget dolor semper
luctus vitae a nulla. Cras semper eros sed lacinia tincidunt. Mauris dignissim ullamcorper dolor,
ut blandit dui ullamcorper faucibus. Interdum et malesuada fames ac ante ipsum.
</ChildContent>
</FluentWizardStep>
<FluentWizardStep OnChange="@OnStepChange">
<StepTemplate>
<div active="@context.Active">
Get Started
</div>
</StepTemplate>
<ChildContent>
<h5>Get Started</h5>
Maecenas sed justo ac sapien venenatis ullamcorper. Sed maximus nunc non venenatis euismod.
Fusce vel porta ex, imperdiet molestie nisl. Vestibulum eu ultricies mauris, eget aliquam quam.
</ChildContent>
</FluentWizardStep>
<FluentWizardStep OnChange="@OnStepChange">
<StepTemplate>
<div active="@context.Active">
Set budget
</div>
</StepTemplate>
<ChildContent>
<h5>Set budget</h5>
Phasellus quis augue convallis, congue velit ac, aliquam ex. In egestas porttitor massa
aliquet porttitor. Donec bibendum faucibus urna vitae elementum. Phasellus vitae efficitur
turpis, eget molestie ipsum.
</ChildContent>
</FluentWizardStep>
<FluentWizardStep OnChange="@OnStepChange">
<StepTemplate>
<div active="@context.Active">
Summary
</div>
</StepTemplate>
<ChildContent>
<h5>Summary</h5>
Ut iaculis sed magna efficitur tempor. Vestibulum est erat, imperdiet in diam ac,
aliquam tempus sapien. Nam rutrum mi at enim mattis, non mollis diam molestie.
Cras sodales dui libero, sit amet cursus sapien elementum ac. Nulla euismod nisi sem.
</ChildContent>
</FluentWizardStep>
</Steps>

<ButtonTemplate>
@{
var index = context;
var lastStepIndex = 3;

<div>
@if (index > 0)
{
<FluentButton OnClick="@(() => MyWizard.GoToStepAsync(0))">Go to first page</FluentButton>
<FluentButton OnClick="@(() => MyWizard.GoToStepAsync(Value - 1))">Previous</FluentButton>
}
</div>
<FluentSpacer />
<div>
@if (index != lastStepIndex)
{
<FluentButton OnClick="@(() => MyWizard.GoToStepAsync(Value + 1))" Appearance="ButtonAppearance.Primary">Next</FluentButton>
<FluentButton OnClick="@(() => MyWizard.GoToStepAsync(lastStepIndex))" Appearance="ButtonAppearance.Primary">Go to last page</FluentButton>
}
else
{
<FluentButton OnClick="@(() => MyWizard.FinishAsync())" Appearance="ButtonAppearance.Primary">Finish</FluentButton>
}
</div>
}
</ButtonTemplate>
</FluentWizard>

@code
{
FluentWizard MyWizard = default!;
int Value = 0;

void OnStepChange(FluentWizardStepChangeEventArgs e)
{
}

async Task OnFinish()
{
await Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<FluentStack VerticalAlignment="VerticalAlignment.Center">
<FluentSwitch @bind-Value="@IsTop"
Style="margin: 30px;"
Label="Step position" />

WizardStepSequence:
<FluentSelect Width="150px"
Items="@(Enum.GetValues<WizardStepSequence>())"
@bind-Value="@StepSequence" />
</FluentStack>

<FluentWizard StepperPosition="@(IsTop ? StepperPosition.Top : StepperPosition.Left)"
StepSequence="@StepSequence"
DisplayStepNumber="@(WizardStepStatus.Current | WizardStepStatus.Next)"
Border="WizardBorder.Outside"
StepTitleHiddenWhen="@GridItemHidden.XsAndDown"
Height="300px"
OnFinish="@OnFinishedAsync">
<Steps>
<FluentWizardStep Label="Intro"
OnChange="@OnStepChange">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ut nisi eget dolor semper
luctus vitae a nulla. Cras semper eros sed lacinia tincidunt. Mauris dignissim ullamcorper dolor,
ut blandit dui ullamcorper faucibus. Interdum et malesuada fames ac ante ipsum.
</FluentWizardStep>
<FluentWizardStep Label="Get started"
Summary="Begin the tasks"
OnChange="@OnStepChange">
Maecenas sed justo ac sapien venenatis ullamcorper. Sed maximus nunc non venenatis euismod.
Fusce vel porta ex, imperdiet molestie nisl. Vestibulum eu ultricies mauris, eget aliquam quam.
</FluentWizardStep>
<FluentWizardStep Disabled="true"
Label="Disabled step"
Summary="This step is disabled"
OnChange="@OnStepChange">
Nunc dignissim tortor eget lacus porta tristique. Nunc in posuere dui. Cras ligula ex,
ullamcorper in gravida in, euismod vitae purus. Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Aliquam at velit leo. Suspendisse potenti. Cras dictum eu augue in laoreet.
</FluentWizardStep>
<FluentWizardStep Label="Set budget"
Summary="Identify the best price"
IconPrevious="@(new Icons.Filled.Size20.Star())"
IconCurrent="@(new Icons.Filled.Size20.StarEmphasis())"
IconNext="@(new Icons.Regular.Size20.Star())"
DisplayStepNumber="false"
OnChange="@OnStepChange">
Phasellus quis augue convallis, congue velit ac, aliquam ex. In egestas porttitor massa
aliquet porttitor. Donec bibendum faucibus urna vitae elementum. Phasellus vitae efficitur
turpis, eget molestie ipsum.
</FluentWizardStep>
<FluentWizardStep Label="Summary"
OnChange="@OnStepChange">
Ut iaculis sed magna efficitur tempor. Vestibulum est erat, imperdiet in diam ac,
aliquam tempus sapien. Nam rutrum mi at enim mattis, non mollis diam molestie.
Cras sodales dui libero, sit amet cursus sapien elementum ac. Nulla euismod nisi sem.
</FluentWizardStep>
</Steps>
</FluentWizard>

@code
{
bool IsTop = false;
WizardStepSequence StepSequence = WizardStepSequence.Linear;

void OnStepChange(FluentWizardStepChangeEventArgs e)
{
}

async Task OnFinishedAsync()
{
await Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
@using System.ComponentModel.DataAnnotations

@inject IDialogService DialogService

<FluentWizard StepperPosition="StepperPosition.Left"
StepSequence="@WizardStepSequence.Visited"
DisplayStepNumber="@(WizardStepStatus.Current | WizardStepStatus.Next)"
Border="WizardBorder.Outside"
StepTitleHiddenWhen="@GridItemHidden.XsAndDown"
Height="auto"
Style="min-height: 300px;"
OnFinish="@OnFinishedAsync">
<Steps>
<FluentWizardStep Label="Personal Info"
OnChange="@OnStepChange">
<EditForm Model="_formData1" FormName="personalInfo" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit">
<DataAnnotationsValidator />
<FluentWizardStepValidator />
<FluentStack Orientation="Orientation.Vertical">
<FluentTextInput Placeholder="First Name" @bind-Value="_formData1.FirstName" />
<FluentTextInput Placeholder="Last Name" @bind-Value="_formData1.LastName" />
</FluentStack>
<FluentValidationSummary style="color:darkred" />
</EditForm>
</FluentWizardStep>
<FluentWizardStep Label="Address Info"
OnChange="@OnStepChange">
<EditForm Model="_formData2" FormName="addressInfo" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit">
<DataAnnotationsValidator />
<FluentWizardStepValidator />
<FluentStack Orientation="Orientation.Vertical">
<FluentTextInput Placeholder="Address Line 1" @bind-Value="_formData2.AddressLine1" />
<FluentTextInput Placeholder="Address Line 2" @bind-Value="_formData2.AddressLine2" />
<FluentTextInput Placeholder="City" @bind-Value="_formData2.City" />
<FluentTextInput Placeholder="State or Province" @bind-Value="_formData2.StateOrProvince" />
<FluentTextInput Placeholder="Country" @bind-Value="_formData2.Country" />
<FluentTextInput Placeholder="Postal Code" @bind-Value="_formData2.PostalCode" />
</FluentStack>
<FluentValidationSummary style="color:darkred" />
</EditForm>
</FluentWizardStep>
<FluentWizardStep Label="Set budget"
Summary="Identify the best price"
IconPrevious="@(new Icons.Filled.Size20.Star())"
IconCurrent="@(new Icons.Filled.Size20.StarEmphasis())"
IconNext="@(new Icons.Regular.Size20.Star())"
DisplayStepNumber="false"
OnChange="@OnStepChange">
Phasellus quis augue convallis, congue velit ac, aliquam ex. In egestas porttitor massa
aliquet porttitor. Donec bibendum faucibus urna vitae elementum. Phasellus vitae efficitur
turpis, eget molestie ipsum.
</FluentWizardStep>
<FluentWizardStep Label="Finish">
<EditForm Model="_finishFormData" FormName="finishForm" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit">
<DataAnnotationsValidator />
<FluentWizardStepValidator />
<FluentStack Orientation="Orientation.Vertical">
<FluentTextInput Label="Signature" Placeholder="Type your signature" @bind-Value="_finishFormData.Signature" />
</FluentStack>
<FluentValidationSummary style="color:darkred" />
</EditForm>
</FluentWizardStep>
</Steps>
</FluentWizard>

@if (_overlayIsVisible)
{
<FluentOverlay @bind-Visible=@_overlayIsVisible
Opacity="40"
Alignment="Align.Center"
Justification="@JustifyContent.Center">
<FluentSpinner />
</FluentOverlay>
}

@code
{
private FormData1 _formData1 = new FormData1();
private FormData2 _formData2 = new FormData2();
private FinishFormData _finishFormData = new FinishFormData();
private bool _overlayIsVisible = false;

void OnStepChange(FluentWizardStepChangeEventArgs e)
{
}

async Task OnFinishedAsync()
{
await DialogService.ShowInfoAsync("Wizard completed");
}

async Task OnValidSubmit()
{
_overlayIsVisible = true;
await Task.Delay(2000);
_overlayIsVisible = false;
}

void OnInvalidSubmit()
{
}

private class FormData1
{
[Required]
[MaxLength(3)]
public string? FirstName { get; set; }

[Required]
[MinLength(10)]
public string? LastName { get; set; }
}

private class FormData2
{
[Required]
public string? AddressLine1 { get; set; }

public string? AddressLine2 { get; set; }

[Required]
public string? City { get; set; }

[Required]
public string? StateOrProvince { get; set; }

[Required]
public string? Country { get; set; }

[Required]
public string? PostalCode { get; set; }
}

private class FinishFormData
{
[Required]
[MinLength(5)]
public string? Signature { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: Wizard
route: /Wizard
icon: Steps
---

# Wizard

**Wizards** are a step-by-step user interface used to break down complex tasks into digestible pieces.
The simplified layout allows the reader to more easily understand the scope of a given task and the actions
needed to complete the task.

By default, steps are displayed on the left, but you can move them to the top of the component.
They are in the form of circular bubbles, with a check mark indicating whether it has been processed or not.
They are not numbered, but the **DisplayStepNumber** property can be used to add this numbering.
It's also possible to customize these bubbles via the **IconPrevious**, **IconCurrent**
and **IconNext** properties.

The order of the steps must be defined when designing the Wizard.
However, it is possible to enable or disable a step via the **Disabled** property.

By default, the contents of all steps are hidden and displayed when the user arrives at that
that step (for display performance reasons). But the **DeferredLoading** property
property reverses this process and generates the contents of the active step only.
Comment on lines +22 to +24
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section has duplicated words (“that … that step”, “property property”) and the DeferredLoading behavior description appears inverted relative to the implementation (in FluentWizard.razor, steps with DeferredLoading=true are rendered only when active). Please fix the typos and clarify that enabling DeferredLoading defers rendering to the active step only (or adjust the code if the current behavior is not intended).

Suggested change
By default, the contents of all steps are hidden and displayed when the user arrives at that
that step (for display performance reasons). But the **DeferredLoading** property
property reverses this process and generates the contents of the active step only.
By default, the contents of all steps are rendered, but only the active step is visible; other steps are shown
when the user navigates to them. When the **DeferredLoading** property is enabled, rendering is deferred so that
only the active step's content is generated.

Copilot uses AI. Check for mistakes.

The **Label** and **Summary** properties display the name and a small summary of the step below or next to the bubble.
The **StepTitleHiddenWhen** property is used to hide this title and summary when the screen width
is reduced, for example on mobile devices. By default, the value `XsAndDown` is applied
to hide this data on cell phones (< 600px).

All these areas (bubbles on the left/top and navigation buttons at the bottom) are fully customizable
using the **StepTemplate** and **ButtonTemplate** properties (see the second example).
You can customize button labels using the **ButtonTemplate** or by modifying
the static properties **FluentWizard.LabelButtonPrevious / LabelButtonNext / LabelButtonDone**.

> **note**: this FluentWizard is not yet fully compatible with accessibility.

{{ WizardDefault }}

## Customized

You can customize the wizard with a **ButtonTemplate** to replace the default Previous/Next/Done buttons,
and **StepTemplate** to fully control how each step indicator is rendered.

{{ WizardCustomized }}

## EditForms

The wizard supports **EditForm** validation. When a step contains an `EditForm`, the wizard will
automatically validate the form before navigating to the next step. If validation fails,
the step change is cancelled.

{{ WizardEditForms }}

## API FluentWizard

{{ API Type=FluentWizard }}

## API FluentWizardStep

{{ API Type=FluentWizardStep }}
Loading