There are several effective ways to share a single model (data object) between multiple child components in Blazor WebAssembly. Here are the best approaches:
1. Cascading Parameters (Best for hierarchical components)
<!-- ParentComponent.razor -->
@page "/parent"
<CascadingValue Value="@SharedModel">
<ChildComponent1 />
<ChildComponent2 />
</CascadingValue>
@code {
private MyModel SharedModel { get; set; } = new MyModel();
}
<!-- ChildComponent1.razor -->
@code {
[CascadingParameter]
public MyModel SharedModel { get; set; }
}
<!-- ChildComponent2.razor -->
@code {
[CascadingParameter]
public MyModel SharedModel { get; set; }
}
2. Component Parameters (Best for direct parent-child relationships)
<!-- ParentComponent.razor -->
@page "/parent"
<ChildComponent1 Model="@SharedModel" />
<ChildComponent2 Model="@SharedModel" />
@code {
private MyModel SharedModel { get; set; } = new MyModel();
}
<!-- ChildComponent1.razor -->
@code {
[Parameter]
public MyModel Model { get; set; }
}
<!-- ChildComponent2.razor -->
@code {
[Parameter]
public MyModel Model { get; set; }
}
3. State Management Service (Best for app-wide sharing)
// SharedModelService.cs
public class SharedModelService
{
private MyModel _model = new();
public MyModel Model
{
get => _model;
set
{
_model = value;
NotifyStateChanged();
}
}
public event Action OnChange;
private void NotifyStateChanged() => OnChange?.Invoke();
}
Register the service in Program.cs
:
builder.Services.AddSingleton<SharedModelService>();
Use in components:
@inject SharedModelService ModelService
@code {
protected override void OnInitialized()
{
ModelService.OnChange += StateHasChanged;
}
private void UpdateModel()
{
ModelService.Model.Property = "New Value";
}
}
4. EventCallback Pattern (For parent-child communication)
<!-- ParentComponent.razor -->
@page "/parent"
<ChildComponent1 Model="@SharedModel" ModelChanged="@HandleModelChanged" />
<ChildComponent2 Model="@SharedModel" ModelChanged="@HandleModelChanged" />
@code {
private MyModel SharedModel { get; set; } = new();
private void HandleModelChanged(MyModel updatedModel)
{
SharedModel = updatedModel;
StateHasChanged(); // Refresh all components
}
}
<!-- ChildComponent1.razor -->
@code {
[Parameter]
public MyModel Model { get; set; }
[Parameter]
public EventCallback<MyModel> ModelChanged { get; set; }
private async Task UpdateModel()
{
Model.Property = "New Value";
await ModelChanged.InvokeAsync(Model);
}
}
5. Fluxor/Redux Pattern (For complex state management)
// Install package
dotnet add package Fluxor.Blazor.Web
// Define state
public record MyModelState
{
public MyModel Model { get; init; } = new();
}
// Define actions
public record UpdateModelAction(MyModel Model);
// Create reducer
public static class Reducers
{
[ReducerMethod]
public static MyModelState ReduceUpdateModelAction(MyModelState state, UpdateModelAction action)
=> state with { Model = action.Model };
}
Use in components:
@inject IState<MyModelState> ModelState
@inject IDispatcher Dispatcher
<p>@ModelState.Value.Model.Property</p>
<button @onclick="UpdateModel">Update</button>
@code {
private void UpdateModel()
{
var updatedModel = ModelState.Value.Model with { Property = "New Value" };
Dispatcher.Dispatch(new UpdateModelAction(updatedModel));
}
}
- For simple parent-child relationships: Use Component Parameters
- For deep component trees: Use Cascading Parameters
- For app-wide state: Use State Management Service or Fluxor
- For complex applications: Consider Fluxor/Redux pattern
- Immutable models: When sharing models, consider making them immutable or implementing proper change notifications
Performance Considerations
- Avoid excessive re-rendering by implementing
ShouldRender
- Use
[Parameter] public MyModel Model { get; set; }
carefully as it can cause unnecessary renders - For large models, consider using view models or DTOs instead of full domain models

