ASP.NET Core Blazor cascading values and parameters

Cascading values and parameters provide a convenient way to flow data down a component hierarchy from an ancestor component to any number of descendent components. Unlike Component parameters, cascading values and parameters don't require an attribute assignment for each descendent component where the data is consumed. Cascading values and parameters also allow components to coordinate with each other across a component hierarchy.

CascadingValue component

An ancestor component provides a cascading value using the Blazor framework's CascadingValue component, which wraps a subtree of a component hierarchy and supplies a single value to all of the components within its subtree.

The following example demonstrates the flow of theme information down the component hierarchy of a layout component to provide a CSS style class to buttons in child components.

The following ThemeInfo C# class is placed in a folder named UIThemeClasses and specifies the theme information.

Note

For the examples in this section, the app's namespace is BlazorSample. When experimenting with the code in your own sample app, change the app's namespace to your sample app's namespace.

UIThemeClasses/ThemeInfo.cs:

namespace BlazorSample.UIThemeClasses
{
    public class ThemeInfo
    {
        public string? ButtonClass { get; set; }
    }
}

The following layout component specifies theme information (ThemeInfo) as a cascading value for all components that make up the layout body of the Body property. ButtonClass is assigned a value of btn-success, which is a Bootstrap button style. Any descendent component in the component hierarchy can use the ButtonClass property through the ThemeInfo cascading value.

Shared/MainLayout.razor:

@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <div class="main">
        <CascadingValue Value="theme">
            <div class="content px-4">
                @Body
            </div>
        </CascadingValue>
    </div>
</div>

@code {
    private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}

[CascadingParameter] attribute

To make use of cascading values, descendent components declare cascading parameters using the [CascadingParameter] attribute. Cascading values are bound to cascading parameters by type. Cascading multiple values of the same type is covered in the Cascade multiple values section later in this article.

The following component binds the ThemeInfo cascading value to a cascading parameter, optionally using the same name of ThemeInfo. The parameter is used to set the CSS class for the Increment Counter (Themed) button.

Pages/ThemedCounter.razor:

@page "/themed-counter"
@using BlazorSample.UIThemeClasses

<h1>Themed Counter</h1>

<p>Current count: @currentCount</p>

<p>
    <button class="btn" @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button 
        class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)" 
        @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    protected ThemeInfo? ThemeInfo { get; set; }

    private void IncrementCount()
    {
        currentCount++;
    }
}

Cascade multiple values

To cascade multiple values of the same type within the same subtree, provide a unique Name string to each CascadingValue component and their corresponding [CascadingParameter] attributes.

In the following example, two CascadingValue components cascade different instances of CascadingType:

<CascadingValue Value="@parentCascadeParameter1" Name="CascadeParam1">
    <CascadingValue Value="@ParentCascadeParameter2" Name="CascadeParam2">
        ...
    </CascadingValue>
</CascadingValue>

@code {
    private CascadingType parentCascadeParameter1;

    [Parameter]
    public CascadingType ParentCascadeParameter2 { get; set; }

    ...
}

In a descendant component, the cascaded parameters receive their cascaded values from the ancestor component by Name:

...

@code {
    [CascadingParameter(Name = "CascadeParam1")]
    protected CascadingType ChildCascadeParameter1 { get; set; }

    [CascadingParameter(Name = "CascadeParam2")]
    protected CascadingType ChildCascadeParameter2 { get; set; }
}

Pass data across a component hierarchy

Cascading parameters also enable components to pass data across a component hierarchy. Consider the following UI tab set example, where a tab set component maintains a series of individual tabs.

Note

For the examples in this section, the app's namespace is BlazorSample. When experimenting with the code in your own sample app, change the namespace to your sample app's namespace.

Create an ITab interface that tabs implement in a folder named UIInterfaces.

UIInterfaces/ITab.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample.UIInterfaces
{
    public interface ITab
    {
        RenderFragment ChildContent { get; }
    }
}

The following TabSet component maintains a set of tabs. The tab set's Tab components, which are created later in this section, supply the list items (<li>...</li>) for the list (<ul>...</ul>).

Child Tab components aren't explicitly passed as parameters to the TabSet. Instead, the child Tab components are part of the child content of the TabSet. However, the TabSet still needs a reference each Tab component so that it can render the headers and the active tab. To enable this coordination without requiring additional code, the TabSet component can provide itself as a cascading value that is then picked up by the descendent Tab components.

Shared/TabSet.razor:

@using BlazorSample.UIInterfaces

<!-- Display the tab headers -->

<CascadingValue Value=this>
    <ul class="nav nav-tabs">
        @ChildContent
    </ul>
</CascadingValue>

<!-- Display body for only the active tab -->

<div class="nav-tabs-body p-4">
    @ActiveTab?.ChildContent
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public ITab ActiveTab { get; private set; }

    public void AddTab(ITab tab)
    {
        if (ActiveTab == null)
        {
            SetActiveTab(tab);
        }
    }

    public void SetActiveTab(ITab tab)
    {
        if (ActiveTab != tab)
        {
            ActiveTab = tab;
            StateHasChanged();
        }
    }
}

Descendent Tab components capture the containing TabSet as a cascading parameter. The Tab components add themselves to the TabSet and coordinate to set the active tab.

Shared/Tab.razor:

@using BlazorSample.UIInterfaces
@implements ITab

<li>
    <a @onclick="ActivateTab" class="nav-link @TitleCssClass" role="button">
        @Title
    </a>
</li>

@code {
    [CascadingParameter]
    public TabSet ContainerTabSet { get; set; }

    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private string TitleCssClass => 
        ContainerTabSet.ActiveTab == this ? "active" : null;

    protected override void OnInitialized()
    {
        ContainerTabSet.AddTab(this);
    }

    private void ActivateTab()
    {
        ContainerTabSet.SetActiveTab(this);
    }
}

The following ExampleTabSet component uses the TabSet component, which contains three Tab components.

Pages/ExampleTabSet.razor:

@page "/example-tab-set"

<TabSet>
    <Tab Title="First tab">
        <h4>Greetings from the first tab!</h4>

        <label>
            <input type="checkbox" @bind="showThirdTab" />
            Toggle third tab
        </label>
    </Tab>

    <Tab Title="Second tab">
        <h4>Hello from the second tab!</h4>
    </Tab>

    @if (showThirdTab)
    {
        <Tab Title="Third tab">
            <h4>Welcome to the disappearing third tab!</h4>
            <p>Toggle this tab from the first tab.</p>
        </Tab>
    }
</TabSet>

@code {
    private bool showThirdTab;
}

Cascading values and parameters provide a convenient way to flow data down a component hierarchy from an ancestor component to any number of descendent components. Unlike Component parameters, cascading values and parameters don't require an attribute assignment for each descendent component where the data is consumed. Cascading values and parameters also allow components to coordinate with each other across a component hierarchy.

CascadingValue component

An ancestor component provides a cascading value using the Blazor framework's CascadingValue component, which wraps a subtree of a component hierarchy and supplies a single value to all of the components within its subtree.

The following example demonstrates the flow of theme information down the component hierarchy of a layout component to provide a CSS style class to buttons in child components.

The following ThemeInfo C# class is placed in a folder named UIThemeClasses and specifies the theme information.

Note

For the examples in this section, the app's namespace is BlazorSample. When experimenting with the code in your own sample app, change the app's namespace to your sample app's namespace.

UIThemeClasses/ThemeInfo.cs:

namespace BlazorSample.UIThemeClasses
{
    public class ThemeInfo
    {
        public string ButtonClass { get; set; }
    }
}

The following layout component specifies theme information (ThemeInfo) as a cascading value for all components that make up the layout body of the Body property. ButtonClass is assigned a value of btn-success, which is a Bootstrap button style. Any descendent component in the component hierarchy can use the ButtonClass property through the ThemeInfo cascading value.

Shared/MainLayout.razor:

@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <div class="main">
        <CascadingValue Value="theme">
            <div class="content px-4">
                @Body
            </div>
        </CascadingValue>
    </div>
</div>

@code {
    private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}

[CascadingParameter] attribute

To make use of cascading values, descendent components declare cascading parameters using the [CascadingParameter] attribute. Cascading values are bound to cascading parameters by type. Cascading multiple values of the same type is covered in the Cascade multiple values section later in this article.

The following component binds the ThemeInfo cascading value to a cascading parameter, optionally using the same name of ThemeInfo. The parameter is used to set the CSS class for the Increment Counter (Themed) button.

Pages/ThemedCounter.razor:

@page "/themed-counter"
@using BlazorSample.UIThemeClasses

<h1>Themed Counter</h1>

<p>Current count: @currentCount</p>

<p>
    <button class="btn" @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button class="btn @ThemeInfo.ButtonClass" @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    protected ThemeInfo ThemeInfo { get; set; }

    private void IncrementCount()
    {
        currentCount++;
    }
}

Cascade multiple values

To cascade multiple values of the same type within the same subtree, provide a unique Name string to each CascadingValue component and their corresponding [CascadingParameter] attributes.

In the following example, two CascadingValue components cascade different instances of CascadingType:

<CascadingValue Value="@parentCascadeParameter1" Name="CascadeParam1">
    <CascadingValue Value="@ParentCascadeParameter2" Name="CascadeParam2">
        ...
    </CascadingValue>
</CascadingValue>

@code {
    private CascadingType parentCascadeParameter1;

    [Parameter]
    public CascadingType ParentCascadeParameter2 { get; set; }

    ...
}

In a descendant component, the cascaded parameters receive their cascaded values from the ancestor component by Name:

...

@code {
    [CascadingParameter(Name = "CascadeParam1")]
    protected CascadingType ChildCascadeParameter1 { get; set; }

    [CascadingParameter(Name = "CascadeParam2")]
    protected CascadingType ChildCascadeParameter2 { get; set; }
}

Pass data across a component hierarchy

Cascading parameters also enable components to pass data across a component hierarchy. Consider the following UI tab set example, where a tab set component maintains a series of individual tabs.

Note

For the examples in this section, the app's namespace is BlazorSample. When experimenting with the code in your own sample app, change the namespace to your sample app's namespace.

Create an ITab interface that tabs implement in a folder named UIInterfaces.

UIInterfaces/ITab.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample.UIInterfaces
{
    public interface ITab
    {
        RenderFragment ChildContent { get; }
    }
}

The following TabSet component maintains a set of tabs. The tab set's Tab components, which are created later in this section, supply the list items (<li>...</li>) for the list (<ul>...</ul>).

Child Tab components aren't explicitly passed as parameters to the TabSet. Instead, the child Tab components are part of the child content of the TabSet. However, the TabSet still needs a reference each Tab component so that it can render the headers and the active tab. To enable this coordination without requiring additional code, the TabSet component can provide itself as a cascading value that is then picked up by the descendent Tab components.

Shared/TabSet.razor:

@using BlazorSample.UIInterfaces

<!-- Display the tab headers -->

<CascadingValue Value=this>
    <ul class="nav nav-tabs">
        @ChildContent
    </ul>
</CascadingValue>

<!-- Display body for only the active tab -->

<div class="nav-tabs-body p-4">
    @ActiveTab?.ChildContent
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public ITab ActiveTab { get; private set; }

    public void AddTab(ITab tab)
    {
        if (ActiveTab == null)
        {
            SetActiveTab(tab);
        }
    }

    public void SetActiveTab(ITab tab)
    {
        if (ActiveTab != tab)
        {
            ActiveTab = tab;
            StateHasChanged();
        }
    }
}

Descendent Tab components capture the containing TabSet as a cascading parameter. The Tab components add themselves to the TabSet and coordinate to set the active tab.

Shared/Tab.razor:

@using BlazorSample.UIInterfaces
@implements ITab

<li>
    <a @onclick="ActivateTab" class="nav-link @TitleCssClass" role="button">
        @Title
    </a>
</li>

@code {
    [CascadingParameter]
    public TabSet ContainerTabSet { get; set; }

    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private string TitleCssClass => 
        ContainerTabSet.ActiveTab == this ? "active" : null;

    protected override void OnInitialized()
    {
        ContainerTabSet.AddTab(this);
    }

    private void ActivateTab()
    {
        ContainerTabSet.SetActiveTab(this);
    }
}

The following ExampleTabSet component uses the TabSet component, which contains three Tab components.

Pages/ExampleTabSet.razor:

@page "/example-tab-set"

<TabSet>
    <Tab Title="First tab">
        <h4>Greetings from the first tab!</h4>

        <label>
            <input type="checkbox" @bind="showThirdTab" />
            Toggle third tab
        </label>
    </Tab>

    <Tab Title="Second tab">
        <h4>Hello from the second tab!</h4>
    </Tab>

    @if (showThirdTab)
    {
        <Tab Title="Third tab">
            <h4>Welcome to the disappearing third tab!</h4>
            <p>Toggle this tab from the first tab.</p>
        </Tab>
    }
</TabSet>

@code {
    private bool showThirdTab;
}

Cascading values and parameters provide a convenient way to flow data down a component hierarchy from an ancestor component to any number of descendent components. Unlike Component parameters, cascading values and parameters don't require an attribute assignment for each descendent component where the data is consumed. Cascading values and parameters also allow components to coordinate with each other across a component hierarchy.

CascadingValue component

An ancestor component provides a cascading value using the Blazor framework's CascadingValue component, which wraps a subtree of a component hierarchy and supplies a single value to all of the components within its subtree.

The following example demonstrates the flow of theme information down the component hierarchy of a layout component to provide a CSS style class to buttons in child components.

The following ThemeInfo C# class is placed in a folder named UIThemeClasses and specifies the theme information.

Note

For the examples in this section, the app's namespace is BlazorSample. When experimenting with the code in your own sample app, change the app's namespace to your sample app's namespace.

UIThemeClasses/ThemeInfo.cs:

namespace BlazorSample.UIThemeClasses
{
    public class ThemeInfo
    {
        public string ButtonClass { get; set; }
    }
}

The following layout component specifies theme information (ThemeInfo) as a cascading value for all components that make up the layout body of the Body property. ButtonClass is assigned a value of btn-success, which is a Bootstrap button style. Any descendent component in the component hierarchy can use the ButtonClass property through the ThemeInfo cascading value.

Shared/MainLayout.razor:

@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses

<div class="sidebar">
    <NavMenu />
</div>

<div class="main">
    <CascadingValue Value="theme">
        <div class="content px-4">
            @Body
        </div>
    </CascadingValue>
</div>

@code {
    private ThemeInfo theme = new ThemeInfo { ButtonClass = "btn-success" };
}

[CascadingParameter] attribute

To make use of cascading values, descendent components declare cascading parameters using the [CascadingParameter] attribute. Cascading values are bound to cascading parameters by type. Cascading multiple values of the same type is covered in the Cascade multiple values section later in this article.

The following component binds the ThemeInfo cascading value to a cascading parameter, optionally using the same name of ThemeInfo. The parameter is used to set the CSS class for the Increment Counter (Themed) button.

Pages/ThemedCounter.razor:

@page "/themed-counter"
@using BlazorSample.UIThemeClasses

<h1>Themed Counter</h1>

<p>Current count: @currentCount</p>

<p>
    <button class="btn" @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button class="btn @ThemeInfo.ButtonClass" @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    protected ThemeInfo ThemeInfo { get; set; }

    private void IncrementCount()
    {
        currentCount++;
    }
}

Cascade multiple values

To cascade multiple values of the same type within the same subtree, provide a unique Name string to each CascadingValue component and their corresponding [CascadingParameter] attributes.

In the following example, two CascadingValue components cascade different instances of CascadingType:

<CascadingValue Value="@parentCascadeParameter1" Name="CascadeParam1">
    <CascadingValue Value="@ParentCascadeParameter2" Name="CascadeParam2">
        ...
    </CascadingValue>
</CascadingValue>

@code {
    private CascadingType parentCascadeParameter1;

    [Parameter]
    public CascadingType ParentCascadeParameter2 { get; set; }

    ...
}

In a descendant component, the cascaded parameters receive their cascaded values from the ancestor component by Name:

...

@code {
    [CascadingParameter(Name = "CascadeParam1")]
    protected CascadingType ChildCascadeParameter1 { get; set; }

    [CascadingParameter(Name = "CascadeParam2")]
    protected CascadingType ChildCascadeParameter2 { get; set; }
}

Pass data across a component hierarchy

Cascading parameters also enable components to pass data across a component hierarchy. Consider the following UI tab set example, where a tab set component maintains a series of individual tabs.

Note

For the examples in this section, the app's namespace is BlazorSample. When experimenting with the code in your own sample app, change the namespace to your sample app's namespace.

Create an ITab interface that tabs implement in a folder named UIInterfaces.

UIInterfaces/ITab.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample.UIInterfaces
{
    public interface ITab
    {
        RenderFragment ChildContent { get; }
    }
}

The following TabSet component maintains a set of tabs. The tab set's Tab components, which are created later in this section, supply the list items (<li>...</li>) for the list (<ul>...</ul>).

Child Tab components aren't explicitly passed as parameters to the TabSet. Instead, the child Tab components are part of the child content of the TabSet. However, the TabSet still needs a reference each Tab component so that it can render the headers and the active tab. To enable this coordination without requiring additional code, the TabSet component can provide itself as a cascading value that is then picked up by the descendent Tab components.

Shared/TabSet.razor:

@using BlazorSample.UIInterfaces

<!-- Display the tab headers -->

<CascadingValue Value=this>
    <ul class="nav nav-tabs">
        @ChildContent
    </ul>
</CascadingValue>

<!-- Display body for only the active tab -->

<div class="nav-tabs-body p-4">
    @ActiveTab?.ChildContent
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public ITab ActiveTab { get; private set; }

    public void AddTab(ITab tab)
    {
        if (ActiveTab == null)
        {
            SetActiveTab(tab);
        }
    }

    public void SetActiveTab(ITab tab)
    {
        if (ActiveTab != tab)
        {
            ActiveTab = tab;
            StateHasChanged();
        }
    }
}

Descendent Tab components capture the containing TabSet as a cascading parameter. The Tab components add themselves to the TabSet and coordinate to set the active tab.

Shared/Tab.razor:

@using BlazorSample.UIInterfaces
@implements ITab

<li>
    <a @onclick="ActivateTab" class="nav-link @TitleCssClass" role="button">
        @Title
    </a>
</li>

@code {
    [CascadingParameter]
    public TabSet ContainerTabSet { get; set; }

    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private string TitleCssClass => 
        ContainerTabSet.ActiveTab == this ? "active" : null;

    protected override void OnInitialized()
    {
        ContainerTabSet.AddTab(this);
    }

    private void ActivateTab()
    {
        ContainerTabSet.SetActiveTab(this);
    }
}

The following ExampleTabSet component uses the TabSet component, which contains three Tab components.

Pages/ExampleTabSet.razor:

@page "/example-tab-set"

<TabSet>
    <Tab Title="First tab">
        <h4>Greetings from the first tab!</h4>

        <label>
            <input type="checkbox" @bind="showThirdTab" />
            Toggle third tab
        </label>
    </Tab>

    <Tab Title="Second tab">
        <h4>Hello from the second tab!</h4>
    </Tab>

    @if (showThirdTab)
    {
        <Tab Title="Third tab">
            <h4>Welcome to the disappearing third tab!</h4>
            <p>Toggle this tab from the first tab.</p>
        </Tab>
    }
</TabSet>

@code {
    private bool showThirdTab;
}