Performance & Optimization

Optimize your Blazor apps with granular state subscriptions and selector patterns.

The Problem: Unnecessary Re-renders

By default, StoreComponent<TState> re-renders when any part of state changes. For large apps, this can cause performance issues.

Performance Impact

In complex applications with frequent state updates, components may re-render hundreds of times per second, even when their displayed data hasn't changed.

The Solution: SelectorStoreComponent

Subscribe to only the data you need:

Before vs After
// ❌ Re-renders on ANY state change
@inherits StoreComponent<AppState>
<h1>@State.Counter</h1>

// ✅ ONLY re-renders when Counter changes
@inherits SelectorStoreComponent<AppState>
<h1>@State.Counter</h1>

@code {
    protected override object SelectState(AppState state) => state.Counter;
}

Performance Impact

Real-world benchmark results from high-frequency update scenarios:

Metric Without Selectors With Selectors
Re-renders/sec ~250 ~10-15
CPU Usage 40-60% 5-10%
Frame Rate 20-30 FPS 60 FPS
Result

Up to 25x fewer re-renders in high-frequency scenarios.

Selector Patterns

Different selector patterns for common use cases:

1

Single Property

Select a single property from your state:

protected override object SelectState(AppState s) => s.UserName;
2

Multiple Properties (Tuple)

Combine multiple properties into a tuple:

protected override object SelectState(AppState s) => (s.UserName, s.IsLoading);
3

Computed Values (Record)

Create computed values from your state:

protected override object SelectState(TodoState s) => new TodoStats(
    Total: s.Todos.Count,
    Completed: s.Todos.Count(t => t.Completed)
);
4

Filtered Collections

Filter collections to only what you need:

protected override object SelectState(TodoState s) =>
    s.Todos.Where(t => !t.Completed).ToImmutableList();

Performance Tips

Best practices for optimal performance:

📦 Batch updates

Instead of multiple sequential updates, combine them into a single update operation.

🔀 Split large stores

Break large stores into focused domains to reduce the scope of state changes.

⏱️ Debounce frequent updates

Use debouncing for high-frequency events like search input, resize, or scroll.

🚀 Lazy load heavy data

Load expensive data on-demand instead of loading everything upfront.

🎯 Use selectors

For components that don't need all state, use SelectorStoreComponent for granular updates.

📊 Profile first

Measure render counts before optimizing. Don't optimize prematurely.

Async Helpers

Check out the Async Helpers section for utilities like UpdateDebounced, UpdateThrottled, and LazyLoad that can help with performance optimization.

Next Steps