When building modern web applications, managing state efficiently becomes crucial. In Blazor, as in other front-end frameworks, state management is key to ensuring that UI updates are predictable, scalable, and easy to maintain. That’s where b.state comes in — a lightweight, pipeline-based state manager designed specifically for Blazor applications.
b.state introduces a fresh, structured approach to handling state by organizing the flow of state changes through a customizable pipeline architecture. This design not only improves clarity and modularity but also enables the use of middleware, allowing developers to inject logic (such as logging, validation, or side-effects) at different stages of the state update process.
If you’re familiar with the Flux pattern (or Redux in the React ecosystem), you’ll find many familiar concepts in b.state, adapted to the Blazor architecture. Both follow a unidirectional data flow and rely on actions to describe state changes. In the table below, you can see a side-by-side comparison to help map concepts between the two approaches.
In this article, we’ll dive into how b.state works, why a pipeline-based model makes sense for state management, and how you can integrate middleware to build more robust and maintainable Blazor applications.
In this introductory tutorial, we’ll replace the default Counter page from the Blazor template using BState. Let’s begin by creating a new Blazor WebAssembly project and adding the following NuGet package:
dotnet add package b-state
In your Program.cs add a basic bstate startup (an advanced setup will be shown in the next story):
builder.Services.AddBState(configuration =>
{
configuration.RegisterFrom(typeof(Program).Assembly);
});
The RegisterFrom method ensure that all Actions and States are automatically registered into application services.
Create a new state called CounterState:
public partial class CounterState(IActionBus actionChannel) : BState(actionChannel)
{
public int Counter { get; private set; } = 100;
}
Thanks to the bstate.analyzer project, your IDE will show any discrepancies from project conventions as warnings:
- Setters must be private inside bstate derived classes
- IAction implementation must be subclasses of a bstate class
The next step is to define the Actions that can modify the CounterState, to do this, I added a CounterState.Actions.cs file with the partial class that contains all the state edit actions, in this case the only possible action is Add
public partial class CounterState
{
record AddAction : IAction;
}
Now it’s time to create the action that will modify the state, i created a new file called CounterState.AddAction.cs:
public partial class CounterState
{
class AddActionHandler(CounterState counterState) : IActionHandler<AddAction>
{
public Task Execute(AddAction request)
{
counterState.Counter++;
return Task.CompletedTask;
}
}
public Task Add() => this.ActionChannel.Send(new AddAction());
}
I implemented IActionHandler as a private class, incrementing the counter in the Execute method.
I also added a public Add() method that internally sends the AddAction.
bstate pipeline flow
When an IAction is executed through the ActionChannel, a pipeline is built to correctly manage the action.
Several elements are added to this pipeline: behaviors (which we will cover next time), preprocessors (also covered next time), the action executor (IActionHandler), and post-processors (which we will discuss later as well).
Once the pipeline execution is complete, a notification process is triggered to inform all components that use the part of the state affected by the action.
bstate component
Now, for the final step of this tutorial, we will create the component.
The component should inherit from BStateComponent and access one or more states by using the UseState method.
This will allow the component to automatically react to any changes in the state and update itself accordingly.
@using bstate.tutorial.Features.Counter
@inherits bstate.core.Components.BStateComponent
<p role="status">Current count: @State.Counter</p>
<button class="btn btn-primary" @onclick="Increment">Click me</button>
@code {
CounterState State => this.UseState<CounterState>();
Task Increment() => this.State.Add();
}
This tutorial is just the first step in introducing how to implement state management using BState in a Blazor application.
In the next parts, we will explore more advanced topics, such as how to use behaviors, processors, and lifecycle extensions to further enhance the capabilities and flexibility of your components.
For a repo example of this tutorial see HERE, for a more complete example HERE
If you found this article helpful, leave a ⭐️ on the library’s repository on GitHub and follow me on GitHub, Twitter and Bluesky for more content.
Thanks for reading!