π Cart
Learn how to identify and fix one of the most common Blazor performance mistakes using Blazor Developer Tools.
π The Problem: Unnecessary Re-renders
This is one of the most common performance mistakes Blazor developers make. When a parent component re-renders, all of its child components re-render tooβeven if their data hasn't changed.
The Scenario
You have a list of product cards. When you update one product's quantity, what happens to the other cards?
@foreach (var product in Products)
{
<ProductCard
Name="@product.Name"
Quantity="@product.Quantity"
QuantityChanged="@HandleChange" />
}What You Expect
Only the changed product card re-renders.
What Actually Happens
ALL product cards re-render! Even the ones whose data didn't change.
With 10 products and a 50ms render time each, changing one quantity takes 500ms instead of 50ms.
π§ͺ Interactive Demo
βοΈ Controls
β Basic (No Optimization)
Apples
¤2.99/lb
Oranges
¤3.49/lb
Lemons
¤1.99/lb
Grapes
¤4.99/lb
In DevTools: All cards have the same Render Count
β Optimized (With ShouldRender)
Apples
¤2.99/lb
Oranges
¤3.49/lb
Lemons
¤1.99/lb
Grapes
¤4.99/lb
In DevTools: Each card has different Render Count. Check ShouldRender Stats!
π Cart Summary
0 itemsYour cart is empty
π How to Detect This Problem with Blazor Developer Tools
Step 1: Open DevTools
Press F12 and navigate to the Blazor tab in your browser's developer tools.
Step 2: Find Your Components
Look for the component tree. Click on individual ProductCardBasic components.
Step 3: Check the Metrics
Look for these key metrics in the details panel:
- Render Count - How many times the component rendered
- Lifecycle Timing β Calls column - Call counts for each lifecycle method
- ShouldRender Stats β True / False - Renders allowed vs skipped
π¨ Red Flags to Look For
β What Healthy Looks Like
β The Solution: Override ShouldRender()
Blazor calls ShouldRender() before every render. By default, it returns true.
Override it to return false when your component's output wouldn't change.
β Before (ProductCardBasic)
@code {
[Parameter]
public int Quantity { get; set; }
// No ShouldRender override
// Component re-renders EVERY time parent does
}β After (ProductCardOptimized)
@code {
[Parameter]
public int Quantity { get; set; }
private int _lastQuantity = -1;
protected override bool ShouldRender()
{
// Only render if OUR data changed
if (Quantity == _lastQuantity)
return false; // Skip render!
_lastQuantity = Quantity;
return true;
}
}Name, Price, and Quantity, check all three!
π¬ Under the Hood: Why Blazor Works This Way
The Render Cycle
When you call StateHasChanged() or a parameter changes, Blazor follows this flow:
- Parent calls StateHasChanged() β Parent queued for re-render
- Parent's BuildRenderTree runs β Generates new virtual DOM
- Blazor diffs the virtual DOM β Finds child components
- For each child: SetParametersAsync() β Parameters passed to child
- Child's ShouldRender() called β Returns true by default
- Child's BuildRenderTree runs β Even if nothing changed!
Why Doesn't Blazor Optimize This Automatically?
Blazor could compare parameters automatically, but:
- Reference types (objects, lists) are compared by reference, not value
- Deep comparison is expensive and error-prone
- You know your component bestβyou decide what matters
The Performance Math
| Scenario | Basic | Optimized |
|---|---|---|
| 4 products, 100ms render | 400ms | 100ms |
| 20 products, 100ms render | 2000ms | 100ms |
| Complexity | O(n) | O(1) |
Other Optimization Strategies
@key Directive
Helps Blazor track list items efficiently during re-ordering.
@foreach (var item in Items)
{ <Item @key="item.Id" /> }Virtualization
Only render visible items in long lists.
<Virtualize Items="Items">
...</Virtualize>ImmutableData
Use immutable objects so reference comparison works.
record Product(int Id, string Name);π Blazor Developer Tools Workflow
Identify
Notice UI sluggishness or check render counts in DevTools
Investigate
Compare Render Count across sibling components
Implement
Add ShouldRender() to skip unnecessary renders
Verify
Confirm False count increases in ShouldRender Stats