| name | fluentui-blazor-usage |
| description | Provides accurate coding patterns for building Blazor applications with the Microsoft.FluentUI.AspNetCore.Components v5 NuGet package. Covers service registration, theming (CSS-based, no FluentDesignTheme), layout, navigation, dialogs, data grid, forms, icons, and common pitfalls.
|
Fluent UI Blazor v5 โ Usage Skill
This skill helps AI assistants generate correct, idiomatic code for
applications that consume the Microsoft.FluentUI.AspNetCore.Components
v5 NuGet package. v5 targets .NET 9+ and wraps
@fluentui/web-components v3.
Critical: v5 is a major rewrite. Many v4 APIs are removed or renamed.
Always verify patterns against this skill before answering.
Quick Setup Checklist
builder.Services.AddFluentUIComponents();
@* App.razor / MainLayout.razor โ add providers once at root *@
<FluentProviders />
<link href="_content/Microsoft.FluentUI.AspNetCore.Components/css/default-fuib.css"
rel="stylesheet" />
The <FluentProviders /> component replaces the need to manually add
<FluentDialogProvider />, <FluentTooltipProvider />, and
<FluentKeyCodeProvider /> separately.
Key v5 Differences from v4
| Area | v4 | v5 |
|---|
| Target framework | net8.0 | net9.0 / net10.0 |
| Theming | <FluentDesignTheme> component | CSS custom properties via JS (setTheme), body[data-theme] attribute |
| Toast / MessageBar | IToastService, IMessageService | Removed โ use FluentToast component directly or custom solution |
| Navigation | FluentNavMenu, FluentNavLink, FluentNavGroup | FluentNav, FluentNavItem, FluentNavCategory, FluentNavSectionHeader |
| Dialog API | IDialogContentComponent<T>, complex reference | Simplified: IDialogService.ShowDialogAsync<TDialog>(DialogOptions) |
| List binding | FluentListBase<TOption>, SelectedOptions | FluentListBase<TOption, TValue>, SelectedItems |
| Progress | FluentProgress | FluentProgressBar (FluentProgress = obsolete shim) |
| Appearance enum | Appearance.Accent, .Stealth, etc. | ButtonAppearance.Primary, .Transparent, etc. |
| Switch | CheckedMessage/UncheckedMessage child content | Removed โ label only via Label parameter |
| Component base | Property injection | Constructor injection of LibraryConfiguration |
| Margin / Padding | Not on components | Available on every component via Margin and Padding parameters |
| Icons packages | Separate NuGet packages | Bundled (core icons), with optional extended packages |
Theming
v5 replaces <FluentDesignTheme> with a JavaScript + CSS custom property model.
Apply a theme
@inject IJSRuntime JSRuntime
<FluentButton OnClick="@SwitchTheme">Toggle theme</FluentButton>
@code {
private async Task SwitchTheme()
{
await JSRuntime.InvokeVoidAsync("Blazor.theme.switchTheme");
}
}
Detect current mode
Blazor.theme.isDarkMode()
Blazor.theme.isSystemDark()
Conditional rendering by theme
<FluentIcon Class="hidden-if-light"
Value="@(new Icons.Filled.Size20.WeatherSunny())" />
<FluentIcon Class="hidden-if-dark"
Value="@(new Icons.Filled.Size20.WeatherMoon())" />
CSS variables
All Fluent design tokens are available as CSS custom properties:
var(--colorBrandBackground), var(--colorNeutralForeground1),
var(--fontSizeBase300), var(--borderRadiusMedium), etc.
C# constants are available via StylesVariables and SystemColors:
style="@($"background: {SystemColors.Neutral.Background1}")"
style="@($"border-radius: {StylesVariables.Borders.Radius.Medium}")"
Layout & Navigation
FluentLayout
<FluentLayout>
<FluentLayoutItem Area="LayoutArea.Header">
<FluentLayoutHamburger />
<span>My App</span>
</FluentLayoutItem>
<FluentLayoutItem Area="LayoutArea.Menu">
<FluentNav>
<FluentNavItem Href="/" Icon="@(new Icons.Regular.Size20.Home())">
Home
</FluentNavItem>
<FluentNavCategory Label="Settings" Icon="@(new Icons.Regular.Size20.Settings())">
<FluentNavItem Href="/settings/profile">Profile</FluentNavItem>
<FluentNavItem Href="/settings/account">Account</FluentNavItem>
</FluentNavCategory>
<FluentNavSectionHeader Label="Admin" />
<FluentNavItem Href="/admin">Dashboard</FluentNavItem>
</FluentNav>
</FluentLayoutItem>
<FluentLayoutItem Area="LayoutArea.Content">
@Body
</FluentLayoutItem>
<FluentLayoutItem Area="LayoutArea.Footer">
<span>© 2025</span>
</FluentLayoutItem>
</FluentLayout>
Common mistake: Using FluentNavMenu / FluentNavLink / FluentNavGroup (v4 names). These do not exist in v5.
Dialogs
Show a simple dialog
@inject IDialogService DialogService
private async Task OpenDialog()
{
var options = new DialogOptions
{
Header = { Title = "Confirm action" },
Size = DialogSize.Medium,
Modal = true,
};
var result = await DialogService.ShowDialogAsync<ConfirmDialog>(options);
if (!result.Cancelled)
{
}
}
Dialog component
@* ConfirmDialog.razor *@
<p>Are you sure you want to proceed?</p>
<FluentButton Appearance="ButtonAppearance.Primary"
OnClick="@Confirm">Yes</FluentButton>
<FluentButton OnClick="@Cancel">No</FluentButton>
@code {
[CascadingParameter]
public IDialogInstance Dialog { get; set; } = default!;
private async Task Confirm()
=> await Dialog.CloseAsync(DialogResult.Ok(true));
private async Task Cancel()
=> await Dialog.CloseAsync(DialogResult.Cancel());
}
Show a drawer
var result = await DialogService.ShowDrawerAsync<DrawerContent>(options =>
{
options.Header.Title = "Settings";
options.Alignment = DialogAlignment.End;
options.Size = DialogSize.Small;
});
Pass parameters to the dialog component
var options = new DialogOptions
{
Header = { Title = "Edit item" },
Parameters = new Dictionary<string, object?>
{
["ItemId"] = selectedItemId,
["ItemName"] = selectedItemName,
},
};
await DialogService.ShowDialogAsync<EditDialog>(options);
Common mistake: Implementing IDialogContentComponent<T> (v4 pattern). In v5, dialog components are plain Razor components that receive IDialogInstance as a cascading parameter.
Data Grid
<FluentDataGrid Items="@people" Pagination="@pagination">
<PropertyColumn Property="@(p => p.Name)" Sortable="true" />
<PropertyColumn Property="@(p => p.Email)" />
<TemplateColumn Title="Actions">
<FluentButton Size="ButtonSize.Small"
OnClick="@(() => Edit(context))">
Edit
</FluentButton>
</TemplateColumn>
</FluentDataGrid>
<FluentPaginator State="@pagination" />
@code {
private IQueryable<Person> people = GetPeople().AsQueryable();
private PaginationState pagination = new() { ItemsPerPage = 10 };
}
Remote data with ItemsProvider
<FluentDataGrid ItemsProvider="@dataProvider" Virtualize="true" ItemSize="46">
<PropertyColumn Property="@(p => p.Name)" Sortable="true" />
</FluentDataGrid>
@code {
private GridItemsProvider<Person> dataProvider = default!;
protected override void OnInitialized()
{
dataProvider = async request =>
{
var result = await Http.GetFromJsonAsync<PagedResult<Person>>(
$"api/people?start={request.StartIndex}&count={request.Count}");
return GridItemsProviderResult.From(result!.Items, result.TotalCount);
};
}
}
See DATAGRID.md for advanced patterns (EF adapter,
OData, column options, resize, custom sort).
Forms & Input Components
v5 input components extend InputBase<TValue> and implement IFluentField,
providing built-in form validation integration.
<EditForm Model="@model" OnValidSubmit="@Submit">
<DataAnnotationsValidator />
<FluentTextField @bind-Value="model.Name"
Label="Name"
Required="true" />
<FluentNumberField @bind-Value="model.Age"
Label="Age"
Min="0" Max="150" />
<FluentSelect TOption="string"
TValue="string"
Items="@countries"
@bind-Value="model.Country"
OptionText="@(c => c)"
OptionValue="@(c => c)"
Label="Country" />
<FluentCheckbox @bind-Value="model.AcceptTerms"
Label="I accept the terms" />
<FluentButton Type="ButtonType.Submit"
Appearance="ButtonAppearance.Primary">
Submit
</FluentButton>
</EditForm>
List components โ two type parameters
All list-based components (FluentSelect, FluentCombobox, FluentListbox)
now take two type parameters: TOption (the item type) and TValue (the
bound value type).
<FluentSelect TOption="Country"
TValue="int"
Items="@countries"
@bind-Value="model.CountryId"
OptionText="@(c => c.Name)"
OptionValue="@(c => c.Id)"
Label="Country" />
@* Multiple selection *@
<FluentListbox TOption="string"
TValue="string"
Items="@tags"
Multiple="true"
SelectedItems="@selectedTags"
SelectedItemsChanged="@(items => selectedTags = items)"
OptionText="@(t => t)"
OptionValue="@(t => t)" />
Common mistake: Using SelectedOptions or SelectedOptionsChanged (v4 names). In v5, use SelectedItems / SelectedItemsChanged.
Icons
Icons are instantiated as objects and passed via Value, IconStart, or
IconEnd parameters:
@* Standalone icon *@
<FluentIcon Value="@(new Icons.Regular.Size24.Add())" />
@* Icon with color *@
<FluentIcon Value="@(new Icons.Filled.Size20.Heart().WithColor("red"))" />
@* Icon in a button *@
<FluentButton IconStart="@(new Icons.Regular.Size20.Add())"
Appearance="ButtonAppearance.Primary">
Add item
</FluentButton>
@* Icon from image URL *@
<FluentIcon Value="@Icon.FromImageUrl("/images/logo.png")" />
Icon naming: Icons.{Variant}.{Size}.{Name}
- Variants:
Regular, Filled
- Sizes:
Size16, Size20, Size24, Size28, Size32, Size48
Margin & Padding (NEW in v5)
Every component supports Margin and Padding parameters using a
spacing-utility syntax:
<FluentCard Margin="Margin.All4" Padding="Padding.Horizontal3 Padding.Vertical2">
Content with spacing
</FluentCard>
Common Pitfalls
- Missing
<FluentProviders /> โ Dialogs, tooltips, and keyboard shortcuts
will silently fail without this component in your layout.
- Using v4 component names โ
FluentNavMenu โ FluentNav,
FluentProgress โ FluentProgressBar, FluentDesignTheme โ removed.
- Using
IToastService โ Toast service does not exist in v5.
SelectedOptions on lists โ Renamed to SelectedItems.
- Single type parameter on lists โ
FluentSelect<string> is v4.
v5 requires FluentSelect<TOption, TValue>.
Appearance.Accent โ Use ButtonAppearance.Primary instead.
- Targeting net8.0 โ v5 requires net9.0 or later.
- Implementing
IDialogContentComponent โ Not needed in v5. Dialog components
are plain Razor components receiving IDialogInstance as a cascading parameter.
Reference Files
- SETUP.md โ Full setup guide for Blazor Server, WebAssembly, and Auto modes
- DATAGRID.md โ Advanced data grid patterns
- THEMING.md โ Detailed theming, design tokens, and CSS variables reference