Cascading Values and Parameters in Blazor
Passing data from a parent component to a child is straightforward — you use [Parameter] properties. But what about passing data to a grandchild, or a component five levels deep? Threading parameters through every intermediate component is tedious and creates brittle code. Cascading values solve this by making data available to the entire subtree without explicit parameter passing.
Basic Cascading Values
A CascadingValue component wraps a subtree and makes a value available to any descendant that asks for it.
<CascadingValue Value="currentTheme">
@Body
</CascadingValue>
@code {
private Theme currentTheme = new Theme { IsDark = true, AccentColour = "#3B82F6" };
}
Any descendant component can receive this value with the [CascadingParameter] attribute:
<!-- SomeDeepChild.razor -->
<div style="color: @theme.AccentColour">
Styled with the cascaded theme.
</div>
@code {
[CascadingParameter]
private Theme? theme { get; set; }
}
The framework matches cascading parameters by type. If you cascade a Theme object, any [CascadingParameter] of type Theme in the subtree will receive it.
Named Cascading Values
When you have multiple cascading values of the same type, you need to distinguish them by name:
<CascadingValue Value="primaryColour" Name="Primary">
<CascadingValue Value="secondaryColour" Name="Secondary">
@Body
</CascadingValue>
</CascadingValue>
@code {
private string primaryColour = "#3B82F6";
private string secondaryColour = "#10B981";
}
Consumers specify which named value they want:
@code {
[CascadingParameter(Name = "Primary")]
private string? PrimaryColour { get; set; }
[CascadingParameter(Name = "Secondary")]
private string? SecondaryColour { get; set; }
}
Fixed vs Dynamic Cascading Values
By default, cascading values are dynamic — when the value changes in the provider, all consumers re-render. If your value never changes after initial setup, mark it as fixed to avoid unnecessary re-renders:
<CascadingValue Value="appConfig" IsFixed="true">
@Body
</CascadingValue>
This is a meaningful performance optimisation. In a large component tree, a dynamic cascading value change can trigger re-renders across hundreds of components. If the value is truly static (like configuration), IsFixed="true" prevents that cascade.
Cascading Values via DI (.NET 8+)
.NET 8 introduced a cleaner approach: registering cascading values through dependency injection. Instead of wrapping your layout in CascadingValue components, you register them in Program.cs:
builder.Services.AddCascadingValue(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
return new AppSettings
{
SiteName = config["SiteName"] ?? "My App",
Version = config["Version"] ?? "1.0"
};
});
This is consumed in exactly the same way as before:
@code {
[CascadingParameter]
private AppSettings? Settings { get; set; }
}
The advantage is that your layout files stay clean, and the cascading value is available everywhere without nesting CascadingValue components.
A Practical Example: User Context
One of the most common uses for cascading values is providing the current user's information throughout the application:
builder.Services.AddCascadingAuthenticationState();
This registers the Task<AuthenticationState> as a cascading value. Any component can then access the current user:
@code {
[CascadingParameter]
private Task<AuthenticationState>? AuthState { get; set; }
private string? username;
protected override async Task OnInitializedAsync()
{
if (AuthState is not null)
{
var state = await AuthState;
username = state.User.Identity?.Name;
}
}
}
When Not to Use Cascading Values
Cascading values are not a replacement for proper state management. They work well for data that is:
- Set once or changes infrequently (theme, configuration, user context)
- Needed by many components across the tree
- Naturally hierarchical
They are a poor fit for data that changes frequently, data that flows upward (child to parent), or application-wide mutable state. For those scenarios, use a state container service registered in DI.
Also be mindful of the re-render implications. A non-fixed cascading value change will cause every consumer to re-render. If you're cascading an object, even a property change on that object (if detected) triggers re-renders. Keep cascading values small and stable.
Summary
Cascading values eliminate prop drilling in Blazor component trees. Use them for relatively static, widely-needed data like themes, configuration, and user context. Prefer the DI registration approach in .NET 8+ for cleaner code. Mark values as IsFixed when they don't change, and resist the temptation to use cascading values as a general state management mechanism.