Themes and Skins¶
The Themes and Skins feature enables you to centrally define and apply consistent styling to all BlazorWebFormsComponents throughout your application—just as you would in ASP.NET Web Forms. This guide explains how to use themes, apply them at runtime, and migrate from Web Forms theme files.
Feature Status
Themes and Skins are implemented in the BlazorWebFormsComponents.Theming namespace and fully available for use.
Overview¶
What Themes and Skins Provide¶
Themes and Skins solve a common problem: maintaining consistent, centralized styling across your entire application without duplicating style definitions on every control.
In Web Forms, you would create .skin files containing control definitions with appearance properties:
<!-- App_Themes/Professional/controls.skin -->
<asp:Button runat="server"
BackColor="#507CD1"
ForeColor="White"
Font-Bold="true" />
Then apply the theme to your page—and every Button would automatically adopt that styling.
With BlazorWebFormsComponents, you achieve the same result using a ThemeConfiguration object and the ThemeProvider wrapper component.
Key Concepts¶
- ThemeConfiguration — A builder object that defines styling rules for controls
- ThemeProvider — A wrapper component that applies a theme to all child controls
- ControlSkin — The styling definition for a single control type (e.g., all Buttons)
- SubStyle — Named style definitions for parts of complex controls (e.g., GridView headers, rows)
- SkinID — A named skin variant for a control (optional; enables multiple skins per control)
- EnableTheming — A control-level property to opt-out of theming
- ThemeMode — Either
StyleSheetTheme(defaults) orTheme(overrides)
Quick Start¶
1. Define Your Theme¶
Create a ThemeConfiguration object that describes how your components should look:
// In App.razor, _Layout.razor, or a dedicated service
var myTheme = new ThemeConfiguration()
.ForControl("Button", skin => skin
.Set(s => s.BackColor, WebColor.FromHtml("#507CD1"))
.Set(s => s.ForeColor, WebColor.FromHtml("#FFFFFF"))
.Set(s => s.Font.Bold, true))
.ForControl("Label", skin => skin
.Set(s => s.ForeColor, WebColor.FromHtml("#333333"))
.Set(s => s.Font.Names, "Arial, sans-serif"));
2. Apply the Theme¶
Wrap your content with <ThemeProvider> to apply the theme:
<ThemeProvider Theme="myTheme">
<Button Text="Themed Button" />
<Label Text="Themed Label" />
</ThemeProvider>
3. Result¶
Every Button and Label inside the ThemeProvider automatically adopts the theme styling. No additional markup changes needed.
Theme Modes¶
Themes support two modes that control how styling precedence works.
Mode 1: StyleSheetTheme (Default)¶
Behavior: Theme acts as defaults. Explicit control properties override the theme.
Use when: You want components to respect their own property values when specified, but fall back to the theme for unspecified properties.
Example:
var theme = new ThemeConfiguration()
.ForControl("Button", skin => skin
.Set(s => s.BackColor, WebColor.FromHtml("#507CD1"))
.Set(s => s.Font.Bold, true));
// In your page
<ThemeProvider Theme="theme" ThemeMode="ThemeMode.StyleSheetTheme">
<!-- Uses theme: BackColor=#507CD1, Bold=true -->
<Button Text="Themed Button" />
<!-- Overrides theme BackColor; keeps theme Bold -->
<Button Text="Custom" BackColor="Red" />
</ThemeProvider>
Mode 2: Theme¶
Behavior: Theme overrides all control properties. Explicit control settings are ignored.
Use when: You want to enforce consistent appearance across your entire application (no exceptions).
Example:
<ThemeProvider Theme="theme" ThemeMode="ThemeMode.Theme">
<!-- Uses theme: BackColor=#507CD1, Bold=true -->
<Button Text="Themed Button" />
<!-- Theme still overrides; BackColor stays #507CD1 -->
<Button Text="Custom" BackColor="Red" />
</ThemeProvider>
Comparison Table¶
| Feature | StyleSheetTheme (Default) | Theme |
|---|---|---|
| Behavior | Sets defaults; explicit values win | Overrides all values |
| Web Forms equivalent | Page.StyleSheetTheme |
Page.Theme |
| Use when | You want components to override theme | You want consistent theming |
| Priority | Component property > Theme | Theme > Component property |
| Flexibility | High—exceptions per control | Low—uniform across app |
Sub-Component Styles¶
Complex controls like GridView, DetailsView, and FormView have sub-components (HeaderStyle, RowStyle, etc.) that need their own styling rules.
Use the SubStyle API to define styles for these sub-components:
var theme = new ThemeConfiguration()
.ForControl("GridView", skin => skin
.SubStyle("HeaderStyle", style => {
style.BackColor = WebColor.FromHtml("#507CD1");
style.ForeColor = WebColor.FromHtml("#FFFFFF");
style.Font.Bold = true;
})
.SubStyle("RowStyle", style => {
style.BackColor = WebColor.FromHtml("#EFF3FB");
})
.SubStyle("AlternatingRowStyle", style => {
style.BackColor = WebColor.FromHtml("#F7F6F3");
})
.SubStyle("FooterStyle", style => {
style.BackColor = WebColor.FromHtml("#507CD1");
style.ForeColor = WebColor.FromHtml("#FFFFFF");
}));
When you render the GridView, all HeaderStyle, RowStyle, AlternatingRowStyle, and FooterStyle elements automatically adopt the theme styling.
Supported Sub-Styles by Control¶
GridView (8 sub-styles): - HeaderStyle, FooterStyle, RowStyle, AlternatingRowStyle, SelectedRowStyle, EditRowStyle, PagerStyle, SortedHeaderStyle
DetailsView (10 sub-styles): - HeaderStyle, FooterStyle, RowStyle, AlternatingRowStyle, SelectedRowStyle, EditRowStyle, InsertRowStyle, CommandRowStyle, PagerStyle, EmptyDataRowStyle
FormView (7 sub-styles): - HeaderStyle, FooterStyle, RowStyle, AlternatingRowStyle, EditRowStyle, InsertRowStyle, PagerStyle
DataGrid (7 sub-styles): - HeaderStyle, FooterStyle, ItemStyle, AlternatingItemStyle, SelectedItemStyle, EditItemStyle, PagerStyle
DataList (5 sub-styles): - HeaderStyle, FooterStyle, ItemStyle, AlternatingItemStyle, SelectedItemStyle
Migration Guide: Web Forms to Blazor¶
Converting .skin Files to ThemeConfiguration¶
Web Forms (.skin File)¶
In Web Forms, you would create a .skin file in your App_Themes folder:
<!-- App_Themes/Professional/controls.skin -->
<asp:Button runat="server"
BackColor="#507CD1"
ForeColor="White"
Font-Bold="true" />
<asp:Label runat="server"
ForeColor="#333333" />
<asp:GridView runat="server">
<HeaderStyle BackColor="#507CD1" ForeColor="White" Font-Bold="True" />
<RowStyle BackColor="#EFF3FB" />
<AlternatingRowStyle BackColor="#F7F6F3" />
</asp:GridView>
Blazor Equivalent¶
In Blazor with BlazorWebFormsComponents, create a ThemeConfiguration:
public static class Themes
{
public static ThemeConfiguration CreateProfessionalTheme()
{
return new ThemeConfiguration()
.ForControl("Button", skin => skin
.Set(s => s.BackColor, WebColor.FromHtml("#507CD1"))
.Set(s => s.ForeColor, WebColor.FromHtml("#FFFFFF"))
.Set(s => s.Font.Bold, true))
.ForControl("Label", skin => skin
.Set(s => s.ForeColor, WebColor.FromHtml("#333333")))
.ForControl("GridView", skin => skin
.SubStyle("HeaderStyle", s => {
s.BackColor = WebColor.FromHtml("#507CD1");
s.ForeColor = WebColor.FromHtml("#FFFFFF");
s.Font.Bold = true;
})
.SubStyle("RowStyle", s => {
s.BackColor = WebColor.FromHtml("#EFF3FB");
})
.SubStyle("AlternatingRowStyle", s => {
s.BackColor = WebColor.FromHtml("#F7F6F3");
}));
}
}
Then use it in your pages:
@page "/"
@inject NavigationManager nav
<ThemeProvider Theme="@theme">
<Button Text="Themed Button" />
<Label Text="Themed Label" />
<GridView ItemsSource="@data">
<!-- GridView header, rows, etc. all inherit theme -->
</GridView>
</ThemeProvider>
@code {
private ThemeConfiguration theme = null!;
protected override void OnInitialized()
{
theme = Themes.CreateProfessionalTheme();
}
}
EnableTheming and Opt-Out¶
By default, all controls participate in theming when wrapped in a <ThemeProvider>. You can opt-out at the control level using the EnableTheming parameter.
Control-Level Opt-Out¶
<ThemeProvider Theme="myTheme">
<!-- This Button uses the theme -->
<Button Text="Themed" />
<!-- This Button does NOT use the theme -->
<Button Text="Custom" EnableTheming="false" />
</ThemeProvider>
Container Propagation¶
When you set EnableTheming="false" on a container (like a Panel), it disables theming for all child controls:
<ThemeProvider Theme="myTheme">
<Button Text="Themed" />
<Panel EnableTheming="false">
<!-- All children are NOT themed -->
<Button Text="Not Themed" />
<Label Text="Not Themed" />
</Panel>
</ThemeProvider>
Named Skins with SkinID¶
The SkinID parameter allows you to define multiple theme variants for the same control type.
Defining Multiple Skins¶
var theme = new ThemeConfiguration()
// Default skin for all Buttons (no SkinID required)
.ForControl("Button", skin => skin
.Set(s => s.BackColor, WebColor.FromHtml("#507CD1"))
.Set(s => s.Font.Bold, true))
// Named skin: "Danger" for destructive actions
.ForControl("Button", "Danger", skin => skin
.Set(s => s.BackColor, WebColor.FromHtml("#DC3545"))
.Set(s => s.ForeColor, WebColor.FromHtml("#FFFFFF")))
// Named skin: "Success" for positive actions
.ForControl("Button", "Success", skin => skin
.Set(s => s.BackColor, WebColor.FromHtml("#28A745"))
.Set(s => s.ForeColor, WebColor.FromHtml("#FFFFFF")));
Using Named Skins¶
<ThemeProvider Theme="theme">
<!-- Uses default Button skin -->
<Button Text="Save" />
<!-- Uses "Danger" skin -->
<Button Text="Delete" SkinID="Danger" />
<!-- Uses "Success" skin -->
<Button Text="Approve" SkinID="Success" />
</ThemeProvider>
Runtime Theme Switching¶
You can switch themes at runtime by assigning a new ThemeConfiguration instance to the ThemeProvider.
Example: Theme Switcher¶
@page "/"
<div>
<button @onclick="() => SwitchTheme(Themes.CreateProfessionalTheme())">
Professional Theme
</button>
<button @onclick="() => SwitchTheme(Themes.CreateModernTheme())">
Modern Theme
</button>
</div>
<ThemeProvider Theme="@currentTheme">
<Button Text="Click me" />
<Label Text="Current theme applied" />
</ThemeProvider>
@code {
private ThemeConfiguration currentTheme = null!;
protected override void OnInitialized()
{
currentTheme = Themes.CreateProfessionalTheme();
}
private void SwitchTheme(ThemeConfiguration newTheme)
{
currentTheme = newTheme;
}
}
When you assign a new theme, all child controls automatically re-render with the new styling.
API Reference¶
ThemeConfiguration¶
The builder class for defining themes.
| Method | Description |
|---|---|
ForControl(string name, Action<ControlSkin> skin) |
Define the default skin for a control type |
ForControl(string name, string skinId, Action<ControlSkin> skin) |
Define a named skin variant for a control type |
ControlSkin¶
Defines styling for a single control.
| Method | Description |
|---|---|
Set<T>(Expression<Func<Style, T>> property, T value) |
Set a style property |
SubStyle(string name, Action<Style> style) |
Define a sub-component style (for complex controls) |
ThemeProvider (Component)¶
Applies a theme to child controls.
| Parameter | Type | Description |
|---|---|---|
Theme |
ThemeConfiguration |
The theme to apply |
ThemeMode |
ThemeMode |
Either StyleSheetTheme (default) or Theme |
ChildContent |
RenderFragment |
Child controls to theme |
Style Properties (Common)¶
All style objects support these properties:
| Property | Type | Description |
|---|---|---|
BackColor |
WebColor? |
Background color |
ForeColor |
WebColor? |
Text color |
BorderColor |
WebColor? |
Border color |
BorderStyle |
BorderStyle? |
Border style (Solid, Dotted, etc.) |
BorderWidth |
Unit? |
Border width |
Width |
Unit? |
Element width |
Height |
Unit? |
Element height |
CssClass |
string? |
CSS class names |
Font |
FontInfo |
Font properties (Bold, Italic, Names, Size, etc.) |
HorizontalAlign |
HorizontalAlign? |
Horizontal alignment (Left, Center, Right) |
VerticalAlign |
VerticalAlign? |
Vertical alignment (Top, Middle, Bottom) |
Wrap |
bool? |
Text wrapping (default: true) |
ThemeMode (Enum)¶
| Value | Description |
|---|---|
StyleSheetTheme |
Theme acts as defaults; component properties override |
Theme |
Theme overrides all component properties |
Best Practices¶
- Centralize Theme Definitions: Create a dedicated
Themes.csstatic class containing all theme configurations - Name Your Skins Meaningfully: Use semantic names like "Danger", "Success", "Warning" rather than "Skin1", "Skin2"
- Test Theme Application: Verify themes render correctly across all affected control types
- Document Theme Intent: Add comments explaining the purpose of each theme variant
- Use StyleSheetTheme for Flexibility: Default to
StyleSheetThememode unless you need strict consistency - Avoid Deep Nesting: Don't nest multiple
ThemeProvidercomponents—apply a single theme at the page or layout level
Troubleshooting¶
Q: Theme not applying to a control
A: Verify the control is wrapped in <ThemeProvider> and the control type name matches exactly (e.g., "Button", not "btn").
Q: Control property overriding the theme when I don't want it to
A: Switch to ThemeMode.Theme to make the theme override all control properties.
Q: Sub-style not applying to GridView rows
A: Ensure the sub-style name matches exactly (e.g., "RowStyle", not "Row" or "Rows"). Verify the GridView is inside a <ThemeProvider>.
Q: EnableTheming="false" not working on a child control
A: Ensure the container (Panel, etc.) where you set EnableTheming="false" is inside a <ThemeProvider>.
See Also¶
- Themes and Skins Migration Strategy — Detailed migration guide from Web Forms theme files
- Styling Components — Style sub-components reference
- GridView — GridView component documentation