# Blazor Performance & Advanced Patterns
## Rendering Optimization
### ShouldRender Override
Control when components re-render to prevent unnecessary rendering cycles.
```csharp
@page "/optimized"
@code {
private int count = 0;
private string value = "test";
protected override bool ShouldRender()
{
// Only render if value changed, not if count changed
// This component doesn't display count directly
return false; // Skip render
}
private void IncrementCount()
{
count++;
// Component won't re-render, child component won't re-render either
}
}
```
### Tracking Changed Fields
```csharp
@page "/tracker"
Name: @name
Age: @age
@code {
private string? name;
private int age;
private bool nameChanged = false;
private bool ageChanged = false;
protected override bool ShouldRender()
{
if (!nameChanged && !ageChanged)
{
return false;
}
nameChanged = false;
ageChanged = false;
return true;
}
private void UpdateName()
{
name = "New Name";
nameChanged = true;
}
private void UpdateAge()
{
age = 30;
ageChanged = true;
}
}
```
### Key Directive for List Items
```csharp
@page "/list"
@foreach (var item in items)
{
}
@code {
private List items = [];
private void AddItem()
{
items = items.Prepend(new Item { Id = Guid.NewGuid(), Name = "New" }).ToList();
}
}
public class Item
{
public Guid Id { get; set; }
public string? Name { get; set; }
}
```
**Why @key matters:**
- Helps Blazor's diffing algorithm match old components to new items
- Prevents component state loss during list reordering
- Improves performance with large lists
### IDisposable for Cleanup
```csharp
@implements IAsyncDisposable
@inject IJSRuntime JS
private IJSObjectReference? module;
private Timer? timer;
protected override async Task OnInitializedAsync()
{
timer = new Timer(_ => UpdateAsync(), null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync(
"import", "./myScript.js");
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
timer?.Dispose();
if (module is not null)
{
await module.DisposeAsync();
}
}
```
## Virtualization
Virtualize large lists to render only visible items.
### Basic Virtualization
```csharp
@page "/large-list"
@using Microsoft.AspNetCore.Components.Web.Virtualization
@item.Id - @item.Name
@code {
private List largeList = [];
protected override void OnInitialized()
{
// Generate 100,000 items
largeList = Enumerable.Range(1, 100000)
.Select(i => new Item { Id = i, Name = $"Item {i}" })
.ToList();
}
}
```
### Async Virtualization (Infinite Scroll)
```csharp
@page "/infinite-scroll"
@using Microsoft.AspNetCore.Components.Web.Virtualization
@item.Name
@code {
private async ValueTask> LoadItems(
ItemsProviderRequest request)
{
// Simulate loading from server
var startIndex = request.StartIndex;
var count = request.Count;
var items = await Service.GetItemsAsync(startIndex, count);
// Return items and total count for scrollbar sizing
return new ItemsProviderResult(items, totalItemCount: 1000000);
}
}
```
**Parameters:**
- `Items` - Static list of items to virtualize
- `ItemsProvider` - Async method to load items on demand
- `OverscanCount` - Extra items to render outside viewport (default 3)
- `ItemSize` - Estimated height for scrollbar calculation
## JavaScript Interop
### Invoke JavaScript from C #
```csharp
@inject IJSRuntime JS
@code {
private async Task CallJavaScript()
{
// Simple call - no return value
await JS.InvokeVoidAsync("console.log", "Hello from Blazor");
// With return value
var result = await JS.InvokeAsync("myFunction", arg1, arg2);
// Generic call with any return type
var data = await JS.InvokeAsync("loadData");
}
}
```
### JS Module Isolation (Recommended)
```csharp
// Component.razor
@implements IAsyncDisposable
@inject IJSRuntime JS
.container {
background-color: blue;
padding: 20px;
}
h1 {
color: white;
font-size: 24px;
}
```
**Benefits:**
- No global namespace pollution
- Component-specific styling
- CSS automatically scoped to component
- Compiled into assembly
## Best Practices Summary
### Performance
- Use `@key` on list items
- Override `ShouldRender()` to prevent unnecessary renders
- Use virtualization for large lists
- Minimize JavaScript interop calls
- Enable AOT compilation and trimming for WASM
### JavaScript Interop
- Use module isolation pattern
- Always dispose JS module references
- Handle JS exceptions properly
- Only call JS in `OnAfterRender` with firstRender check
- Minimize interop calls for performance
### Architecture
- Keep components simple and focused
- Move logic to services
- Use cascading values for shared state
- Implement IDisposable for cleanup
- Validate authorization on server side
### User Experience
- Show loading states during async operations
- Provide error feedback
- Use AuthorizeView for conditional rendering
- Implement error boundaries
- Test on slow connections
---
**Related Resources:** See [components-lifecycle.md](components-lifecycle.md) for component disposal patterns. See [state-management-events.md](state-management-events.md) for state update optimization.