100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C#

State Management Patterns

Explore techniques for sharing and persisting state across Blazor components, from cascading parameters to dedicated state containers.

State & ServicesIntermediate10 min readJul 10, 2026
Analogies

Why State Management Matters in Blazor

State in a Blazor app is any data that must survive beyond a single method call and be visible to more than one component—things like the logged-in user, cart contents, or a wizard's current step. Because Blazor UIs are trees of independently rendered components, passing state as parameters works for direct parent-child relationships, but sibling or deeply nested components need a shared mechanism: cascading values, injected state container services, or browser storage, each with different scope and lifetime trade-offs.

🏏

Cricket analogy: A DRS decision must be visible to the umpires, both captains, and the broadcast graphics simultaneously, not just passed privately between two fielders—much like Blazor state that must reach components beyond a direct parent-child link.

Cascading Parameters and CascadingValue

CascadingValue is a built-in Blazor component that makes a value available to every descendant in its render subtree without explicitly threading it through each intermediate component's parameters. A component anywhere below the CascadingValue in the tree opts in with [CascadingParameter], and Blazor automatically re-renders subscribers whenever the cascaded value changes, which is well suited to app-wide concerns like the current theme, authenticated user, or a wizard step, but becomes unwieldy for state with complex mutation logic.

🏏

Cricket analogy: A stadium-wide PA announcement about a rain delay reaches every stand automatically without the announcer visiting each section individually, just as CascadingValue reaches every descendant component without explicit parameter threading.

State Container Services with Events

A dedicated state container is a plain injected service, typically Scoped, that exposes properties plus a NotifyStateChanged event or C# event delegate; components subscribe in OnInitialized and call StateHasChanged in the event handler, then unsubscribe in Dispose to avoid memory leaks. This pattern decouples state mutation from any specific component, keeps the mutation logic testable in isolation from the UI, and scales better than cascading values once the state has real behavior, like validation or undo history.

🏏

Cricket analogy: A team's official scorer service maintains the single source of truth for runs and wickets, and every broadcaster's graphics team subscribes to updates from it rather than each maintaining a private tally, just like a state container service.

csharp
public class CartStateService
{
    private readonly List<CartItem> _items = new();
    public IReadOnlyList<CartItem> Items => _items;
    public event Action? OnChange;

    public void AddItem(CartItem item)
    {
        _items.Add(item);
        OnChange?.Invoke();
    }
}
csharp
@implements IDisposable
@inject CartStateService CartState

<p>Items in cart: @CartState.Items.Count</p>

@code {
    protected override void OnInitialized()
    {
        CartState.OnChange += StateHasChanged;
    }

    public void Dispose()
    {
        CartState.OnChange -= StateHasChanged;
    }
}

CascadingValue works well for read-mostly, app-wide values like the current culture or authenticated user, but for state with real mutation logic — like a shopping cart with add/remove/undo — a dedicated injected state container service scales better and is easier to unit test.

Forgetting to unsubscribe from a state container's OnChange event in Dispose leaks a reference to the component, keeping it (and its render tree) alive in memory even after Blazor has removed it from the UI, especially damaging in long-running Blazor Server circuits.

  • State that crosses component boundaries needs a mechanism beyond simple parent-to-child parameters
  • CascadingValue + [CascadingParameter] broadcasts a value to every descendant in the render subtree automatically
  • Dedicated state container services (usually Scoped) expose state plus a change-notification event
  • Components subscribe to state changes in OnInitialized and must unsubscribe in Dispose to prevent leaks
  • State containers decouple mutation logic from the UI, making it independently testable
  • Cascading values suit simple, app-wide, read-mostly data; state containers suit state with real behavior
  • In Blazor Server, Scoped state containers live for the circuit; in WASM they live for the app session

Practice what you learned

Was this page helpful?

Topics covered

#BlazorStudyNotes#MicrosoftTechnologies#StateManagementPatterns#State#Management#Patterns#Matters#StudyNotes#SkillVeris#ExamPrep