In Blazor, cascading parameters enable developers to pass data across multiple levels of a component tree without explicitly passing it through each intermediate component (prop drilling). This tutorial introduces the following aspects of using cascading parameters:
CascadingValue
componentIn Blazor, cascading parameters can be implemented in 2 primary ways: registering them at the root level in Program.cs
(root-level technique) or using the CascadingValue
component within specific parts of the component tree. Each approach has distinct characteristics, as outlined in the comparison table below:
Aspect | Root Level Technique | CascadingValue Component |
---|---|---|
Overriding Parameter Value | No | Yes |
Refresh UI When the Value Changes | Automatically | Manual |
Declaration Location | Program.cs |
Any Component |
Program.cs
, it becomes available to all components in the app. This method automatically updates the UI when the value changes but doesn’t allow overriding at lower levels.CascadingValue
Component: Using CascadingValue
within a component allows for more control, enabling you to override the parameter in specific branches of the tree. However, UI updates require manual triggering (e.g., via StateHasChanged
).Do not use the root level technique as a way to register a singleton/scoped service. The root level cascading parameter is designed only for passing parameters, not for passing services. If you need a service, use the dependency injection feature instead.
You can register cascading parameters in Program.cs to provide site-wide data, it is particularly effective for scenarios like theme settings or account information, where a single value needs to be shared across the entire app.
RootLevelSampleModel
) as the cascading parameter, you're not limited to classes. The root level technique also supports registering struct-typed values, such as string
, int
, bool
, and more.public class RootLevelSampleModel { public string Message { get; set; } = ""; }
Program.cs
.builder.Services.AddCascadingValue<RootLevelSampleModel>(sp => new RootLevelSampleModel { Message = "Hello Blazor School!" });
To register 2 parameters of the same type, use the Name
parameter to differentiate them:
builder.Services.AddCascadingValue<RootLevelSampleModel>("Variant2", sp => new RootLevelSampleModel { Message = "Hello Blazor School! (Variant2)" });
<div>Message: @Sample?.Message</div> <div>Variant2 Message: @SampleVariant2?.Message</div> @code { [CascadingParameter] public RootLevelSampleModel? Sample { get; set; } [CascadingParameter(Name = "Variant2")] public RootLevelSampleModel? SampleVariant2 { get; set; } }
CascadingValueSource<T>
where T
is the parameter model itself.public class RootLevelUpdatingModel { public string Message { get; set; } = ""; public CascadingValueSource<RootLevelUpdatingModel>? Source { get; set; } }
Program.cs
.builder.Services.AddCascadingValue(sp => { var value = new RootLevelUpdatingModel() { Message = "Hello Blazor School! (With Notifying)", }; value.Source = new CascadingValueSource<RootLevelUpdatingModel>(value, false); return value.Source; });
NotifyChangeAsync
from CascadingValueSource
.<h3>Component2 (Level 1)</h3> <div>Message: @Sample?.Message</div> <button type="button" @onclick="UpdateValueAsync">Update Value</button> @code { [CascadingParameter] public RootLevelUpdatingModel? Sample { get; set; } public async Task UpdateValueAsync() { if (Sample is not null && Sample.Source is not null) { Sample.Message = "Overridden from Component2"; await Sample.Source.NotifyChangedAsync(); } } }
CascadingValue
ComponentThe CascadingValue
component allows you to pass parameters across a component tree, with the added flexibility of updating and overriding them at different levels. This technique is particularly useful for scenarios like multilingual websites, where different sections might display distinct languages simultaneously.
public class UsingCascadingValueModel { public string Message { get; set; } = ""; }
CascadingValue
in an ancestor component.<h3>Ancestor (Level 0)</h3> <CascadingValue Value="Model" IsFixed="true"> <Component1 /> </CascadingValue> @code { public UsingCascadingValueModel Model { get; set; } = new() { Message = "Hello from SimpleCascadingValueExample" }; }
In this example, we are not updating the parameter, so we set IsFixed
to true
. Setting IsFixed
to true
will not allow the parameter to be updated but will yield better performance.
<h3>Component1 (Level 0)</h3> <div>Message: @Sample?.Message</div> @code { [CascadingParameter] public UsingCascadingValueModel? Sample { get; set; } }
In case you are passing multiple parameters of the same type, set a name when passing them with CascadingValue
:
<CascadingValue Value="Model" IsFixed="true" Name="blazorschool_parameter"> <!-- ... --> </CascadingValue>
Then, in your descendant component, use the name to differentiate the parameters:
@code { [CascadingParameter(Name = "blazorschool_parameter")] public UsingCascadingValueModel? Sample { get; set; } }
A parameter passed by CascadingValue
can be updated at any level in the hierarchy.
this
) using nested CascadingValue
.<CascadingValue Value="this"> <CascadingValue Value="Model"> <Component1 /> </CascadingValue> </CascadingValue> @code { public UsingCascadingValueModel Model { get; set; } = new() { Message = "Hello from UpdateCascadingValueExample" }; public void UpdateModelMessage(string message) { Model.Message = message; StateHasChanged(); } }
<h3>Component2 (level 1)</h3> <div>Message: @Sample?.Message</div> <button type="button" @onclick="UpdateValueAsync">Update Value</button> @code { [CascadingParameter] public UsingCascadingValueModel? Sample { get; set; } [CascadingParameter] public UpdateCascadingValueExample? Source { get; set; } public async Task UpdateValueAsync() { if (Source is not null) { Source.UpdateModelMessage("Update from Component2"); } } }
A parameter passed by CascadingValue
can be overridden to have different values in different branches. Furthermore, a descendant component can override the parameter value in any ancestor component.
We will set up the example with 4 levels: OverrideCascadingValueExample
as the ancestor component, Component1
as Level 0, Component2
as Level 1, and Component3
as Level 2. The parameter will be passed from OverrideCascadingValueExample
and will be overridden in Component2
, and then updated in Component3
.
CascadingValue
.<CascadingValue Value="this"> <CascadingValue Value="Model"> <Component1 /> </CascadingValue> </CascadingValue> @code { public UsingCascadingValueModel Model { get; set; } = new() { Message = "Hello from OverrideCascadingValueExample" }; public void UpdateModel(string message) { Model.Message = message; StateHasChanged(); } }
this
) and the overridden parameter using CascadingValue
.<h3>Component2 (level 1)</h3> <div>Message: @OverridingSample.Message</div> <CascadingValue Value="this"> <CascadingValue Value="OverridingSample"> <Component3 /> </CascadingValue> </CascadingValue> @code { public UsingCascadingValueModel OverridingSample { get; set; } = new UsingCascadingValueModel() { Message = "Overridden from Component2" }; public void UpdateModel(string message) { OverridingSample.Message = message; StateHasChanged(); } }
<h3>Component3 (level 2)</h3> <div>Message: @Sample?.Message</div> <button type="button" @onclick="UpdateAtLevel1">Update at level 1</button> <button type="button" @onclick="UpdateAtRoot">Update at root</button> @code { [CascadingParameter] public UsingCascadingValueModel? Sample { get; set; } [CascadingParameter] public Component2? Component2 { get; set; } [CascadingParameter] public OverrideCascadingValueExample? Root { get; set; } public void UpdateAtLevel1() { Component2?.UpdateModel("Updated from Component3"); } public void UpdateAtRoot() { Root?.UpdateModel("Updated from Component3"); } }