
Previous article: ASP.NET Core Razor Pages Mastery: Advanced UI Patterns, Real-World Examples & Performance Optimization(Part-21 of 40)
📚 Table of Contents
Introduction to Blazor Superpowers
Blazor Architecture Deep Dive
Component-Based Development
Real-Time Features with SignalR
Blazor Server vs WebAssembly
Advanced Component Patterns
State Management Strategies
Performance Optimization
Real-World Application
Best Practices & Deployment
1. Introduction to Blazor Superpowers
1.1 What is Blazor?
Blazor is a revolutionary framework that enables building interactive web UIs using C# instead of JavaScript. It represents a paradigm shift in web development for .NET developers.
// Traditional JavaScript approach
document.getElementById('message').innerHTML = 'Hello World';
// Blazor C# approach
<h1>@message</h1>
@code {
private string message = "Hello World";
}
1.2 Why Blazor Matters
Real-Life Scenario: Imagine building a financial dashboard that updates stock prices in real-time without page refreshes, using only C# skills your team already possesses.
// Real-time stock ticker component
<div class="stock-ticker">
@foreach (var stock in stocks)
{
<div class="stock-item @(stock.IsRising ? "rising" : "falling")">
<span>@stock.Symbol</span>
<span>@stock.Price.ToString("C")</span>
<span>@stock.ChangePercentage.ToString("P")</span>
</div>
}
</div>
1.3 Blazor Hosting Models
1.3.1 Blazor Server
// Program.cs for Blazor Server
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
1.3.2 Blazor WebAssembly
// Program.cs for Blazor WebAssembly
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddHttpClient();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
2. Blazor Architecture Deep Dive
2.1 Component Lifecycle
Understanding the component lifecycle is crucial for building robust applications:
public class LifecycleDemo : ComponentBase
{
// 1. SetParametersAsync - First lifecycle method
public override async Task SetParametersAsync(ParameterView parameters)
{
Console.WriteLine("SetParametersAsync - Setting component parameters");
await base.SetParametersAsync(parameters);
}
// 2. OnInitialized / OnInitializedAsync
protected override void OnInitialized()
{
Console.WriteLine("OnInitialized - Component initialized");
base.OnInitialized();
}
protected override async Task OnInitializedAsync()
{
Console.WriteLine("OnInitializedAsync - Async initialization");
await LoadDataAsync();
}
// 3. OnParametersSet / OnParametersSetAsync
protected override void OnParametersSet()
{
Console.WriteLine("OnParametersSet - Parameters set");
base.OnParametersSet();
}
// 4. OnAfterRender / OnAfterRenderAsync
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
Console.WriteLine("OnAfterRender - First render complete");
// Initialize JavaScript interop here
}
base.OnAfterRender(firstRender);
}
// 5. ShouldRender - Control re-rendering
protected override bool ShouldRender()
{
Console.WriteLine("ShouldRender - Deciding whether to render");
return base.ShouldRender();
}
private async Task LoadDataAsync()
{
// Simulate data loading
await Task.Delay(1000);
}
// Dispose pattern for cleanup
public void Dispose()
{
Console.WriteLine("Dispose - Cleaning up resources");
}
}
2.2 Rendering Process
Blazor uses a sophisticated rendering process:
// Custom component demonstrating rendering
<div class="render-demo">
<h3>Render Counter: @renderCount</h3>
<button @onclick="IncrementCount" class="btn btn-primary">
Trigger Render
</button>
@if (showAdditionalContent)
{
<div class="additional-content">
<p>This content conditionally renders</p>
<ChildComponent Message="Hello from parent" />
</div>
}
</div>
@code {
private int renderCount = 0;
private bool showAdditionalContent = false;
private int currentCount = 0;
protected override void OnAfterRender(bool firstRender)
{
renderCount++;
StateHasChanged(); // This would cause infinite loop - don't do this!
}
private void IncrementCount()
{
currentCount++;
showAdditionalContent = currentCount % 2 == 0;
// StateHasChanged() is automatically called for event handlers
}
}
// Child component
<div class="child-component">
<p>@Message - Rendered: @DateTime.Now.ToString("HH:mm:ss.fff")</p>
</div>
@code {
[Parameter]
public string Message { get; set; } = string.Empty;
}
2.3 Event Handling System
Blazor provides a comprehensive event handling system:
@page "/event-demo"
<div class="event-demo-container">
<h3>Event Handling Examples</h3>
<!-- Mouse Events -->
<div class="mouse-events">
<div class="interactive-box"
@onmouseover="HandleMouseOver"
@onmouseout="HandleMouseOut"
@onclick="HandleClick"
@oncontextmenu="HandleContextMenu"
style="width: 200px; height: 100px; background-color: @boxColor; display: flex; align-items: center; justify-content: center;">
Interact with me!
</div>
<p>Event Status: @eventStatus</p>
</div>
<!-- Keyboard Events -->
<div class="keyboard-events">
<input @onkeydown="HandleKeyDown"
@onkeyup="HandleKeyUp"
@oninput="HandleInput"
placeholder="Type something..."
class="form-control" />
<p>Last Key: @lastKey | Input Value: @inputValue</p>
</div>
<!-- Form Events -->
<div class="form-events">
<select @onchange="HandleSelectChange" class="form-select">
<option value="">Choose an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
<p>Selected: @selectedValue</p>
<input type="checkbox" @onchange="HandleCheckboxChange" />
<span>Checkbox is @(isChecked ? "checked" : "unchecked")</span>
</div>
<!-- Custom Event Arguments -->
<div class="custom-events">
<button @onclick:stopPropagation="HandleButtonClick"
class="btn btn-info">
Click (No Propagation)
</button>
<button @onclick="async () => await HandleAsyncClick()"
class="btn btn-warning">
Async Click
</button>
</div>
</div>
@code {
private string eventStatus = "No events yet";
private string boxColor = "lightblue";
private string lastKey = "None";
private string inputValue = "";
private string selectedValue = "";
private bool isChecked = false;
private void HandleMouseOver()
{
eventStatus = "Mouse Over";
boxColor = "lightgreen";
}
private void HandleMouseOut()
{
eventStatus = "Mouse Out";
boxColor = "lightblue";
}
private void HandleClick(MouseEventArgs e)
{
eventStatus = $"Clicked at ({e.ClientX}, {e.ClientY})";
}
private void HandleContextMenu(MouseEventArgs e)
{
eventStatus = "Context Menu prevented";
// Prevent default context menu
}
private void HandleKeyDown(KeyboardEventArgs e)
{
lastKey = $"KeyDown: {e.Key} (Code: {e.Code})";
}
private void HandleKeyUp(KeyboardEventArgs e)
{
lastKey = $"KeyUp: {e.Key}";
}
private void HandleInput(ChangeEventArgs e)
{
inputValue = e.Value?.ToString() ?? "";
}
private void HandleSelectChange(ChangeEventArgs e)
{
selectedValue = e.Value?.ToString() ?? "None";
}
private void HandleCheckboxChange(ChangeEventArgs e)
{
isChecked = (bool)(e.Value ?? false);
}
private void HandleButtonClick()
{
eventStatus = "Button clicked (propagation stopped)";
}
private async Task HandleAsyncClick()
{
eventStatus = "Async operation starting...";
StateHasChanged(); // Force immediate UI update
await Task.Delay(1000); // Simulate async work
eventStatus = "Async operation completed!";
}
}
3. Component-Based Development
3.1 Component Fundamentals
Components are the building blocks of Blazor applications:
// ProductCard.razor
<div class="product-card">
<div class="product-image">
<img src="@Product.ImageUrl" alt="@Product.Name" />
@if (Product.IsOnSale)
{
<span class="sale-badge">SALE</span>
}
</div>
<div class="product-info">
<h3>@Product.Name</h3>
<p class="description">@Product.Description</p>
<div class="pricing">
@if (Product.IsOnSale)
{
<span class="original-price">@Product.Price.ToString("C")</span>
<span class="sale-price">@Product.SalePrice.ToString("C")</span>
}
else
{
<span class="price">@Product.Price.ToString("C")</span>
}
</div>
<div class="rating">
@for (int i = 1; i <= 5; i++)
{
<span class="star @(i <= Product.Rating ? "filled" : "")">★</span>
}
<span class="rating-count">(@Product.ReviewCount)</span>
</div>
<div class="actions">
<button class="btn btn-primary" @onclick="AddToCart">
Add to Cart
</button>
<button class="btn btn-outline-secondary" @onclick="ToggleFavorite">
@(IsFavorite ? "❤️" : "🤍")
</button>
</div>
</div>
</div>
@code {
[Parameter]
public Product Product { get; set; } = new();
[Parameter]
public EventCallback<Product> OnAddToCart { get; set; }
[Parameter]
public EventCallback<Product> OnToggleFavorite { get; set; }
private bool IsFavorite { get; set; }
private async Task AddToCart()
{
if (OnAddToCart.HasDelegate)
{
await OnAddToCart.InvokeAsync(Product);
}
}
private async Task ToggleFavorite()
{
IsFavorite = !IsFavorite;
if (OnToggleFavorite.HasDelegate)
{
await OnToggleFavorite.InvokeAsync(Product);
}
}
}
// Supporting classes
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public decimal Price { get; set; }
public decimal SalePrice { get; set; }
public bool IsOnSale { get; set; }
public string ImageUrl { get; set; } = string.Empty;
public int Rating { get; set; }
public int ReviewCount { get; set; }
public string Category { get; set; } = string.Empty;
}
3.2 Advanced Component Patterns
3.2.1 Render Fragments
// CardComponent.razor
<div class="card @AdditionalCss">
@if (HeaderContent != null)
{
<div class="card-header">
@HeaderContent
</div>
}
<div class="card-body">
@if (BodyContent != null)
{
@BodyContent
}
else
{
<p>Default body content</p>
}
</div>
@if (FooterContent != null)
{
<div class="card-footer">
@FooterContent
</div>
}
</div>
@code {
[Parameter]
public string AdditionalCss { get; set; } = string.Empty;
[Parameter]
public RenderFragment? HeaderContent { get; set; }
[Parameter]
public RenderFragment? BodyContent { get; set; }
[Parameter]
public RenderFragment? FooterContent { get; set; }
}
// Usage in parent component
<CardComponent AdditionalCss="shadow-lg">
<HeaderContent>
<h4>Custom Header</h4>
<small>With additional information</small>
</HeaderContent>
<BodyContent>
<p>This is custom body content</p>
<button class="btn btn-primary">Action</button>
</BodyContent>
<FooterContent>
<div class="text-muted">Custom footer content</div>
</FooterContent>
</CardComponent>
3.2.2 Cascading Parameters
// ThemeProvider.razor
<CascadingValue Value="CurrentTheme" IsFixed="false">
@ChildContent
</CascadingValue>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
private Theme currentTheme = new();
public Theme CurrentTheme
{
get => currentTheme;
set
{
currentTheme = value;
StateHasChanged();
}
}
public void SetTheme(Action<Theme> themeAction)
{
themeAction(currentTheme);
StateHasChanged();
}
}
// ThemedButton.razor
<button class="btn"
style="background-color: @Theme.PrimaryColor; color: @Theme.TextColor;"
@onclick="OnClick"
@attributes="AdditionalAttributes">
@ChildContent
</button>
@code {
[CascadingParameter]
protected Theme Theme { get; set; } = new();
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public EventCallback OnClick { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object> AdditionalAttributes { get; set; } = new();
}
// Theme class
public class Theme
{
public string PrimaryColor { get; set; } = "#007bff";
public string SecondaryColor { get; set; } = "#6c757d";
public string TextColor { get; set; } = "#ffffff";
public string BackgroundColor { get; set; } = "#ffffff";
public string BorderRadius { get; set; } = "4px";
}
3.3 Component Communication
3.3.1 Parent-Child Communication
// ParentComponent.razor
@page "/parent-demo"
<div class="parent-container">
<h3>Parent-Child Communication Demo</h3>
<div class="controls">
<button @onclick="AddNewItem" class="btn btn-success">
Add New Item
</button>
<button @onclick="ClearAll" class="btn btn-danger">
Clear All
</button>
</div>
<ChildComponent
Items="itemList"
OnItemSelected="HandleItemSelected"
OnItemDeleted="HandleItemDeleted"
OnItemsChanged="HandleItemsChanged" />
<div class="status">
<p>Selected Item: @selectedItem</p>
<p>Total Items: @itemList.Count</p>
<p>Last Action: @lastAction</p>
</div>
</div>
@code {
private List<string> itemList = new() { "Item 1", "Item 2", "Item 3" };
private string selectedItem = "None";
private string lastAction = "None";
private void AddNewItem()
{
itemList.Add($"Item {itemList.Count + 1}");
lastAction = $"Added item at {DateTime.Now:HH:mm:ss}";
}
private void ClearAll()
{
itemList.Clear();
selectedItem = "None";
lastAction = $"Cleared all items at {DateTime.Now:HH:mm:ss}";
}
private void HandleItemSelected(string item)
{
selectedItem = item;
lastAction = $"Selected: {item} at {DateTime.Now:HH:mm:ss}";
}
private void HandleItemDeleted(string item)
{
itemList.Remove(item);
lastAction = $"Deleted: {item} at {DateTime.Now:HH:mm:ss}";
}
private void HandleItemsChanged(List<string> items)
{
// This demonstrates two-way binding pattern
itemList = items;
lastAction = $"Items changed at {DateTime.Now:HH:mm:ss}";
}
}
// ChildComponent.razor
<div class="child-container">
<h4>Child Component</h4>
<ul class="item-list">
@foreach (var item in Items)
{
<li class="item">
<span @onclick="() => SelectItem(item)"
class="item-text @(selectedItem == item ? "selected" : "")">
@item
</span>
<button @onclick="() => DeleteItem(item)"
class="btn btn-sm btn-outline-danger">
Delete
</button>
</li>
}
</ul>
@if (Items.Count == 0)
{
<p class="text-muted">No items available</p>
}
<div class="child-controls">
<button @onclick="ReverseItems" class="btn btn-warning">
Reverse Items
</button>
</div>
</div>
@code {
[Parameter]
public List<string> Items { get; set; } = new();
[Parameter]
public EventCallback<string> OnItemSelected { get; set; }
[Parameter]
public EventCallback<string> OnItemDeleted { get; set; }
[Parameter]
public EventCallback<List<string>> OnItemsChanged { get; set; }
private string selectedItem = string.Empty;
private async Task SelectItem(string item)
{
selectedItem = item;
if (OnItemSelected.HasDelegate)
{
await OnItemSelected.InvokeAsync(item);
}
}
private async Task DeleteItem(string item)
{
if (OnItemDeleted.HasDelegate)
{
await OnItemDeleted.InvokeAsync(item);
}
}
private async Task ReverseItems()
{
Items.Reverse();
if (OnItemsChanged.HasDelegate)
{
await OnItemsChanged.InvokeAsync(Items);
}
}
}
4. Real-Time Features with SignalR
4.1 SignalR Integration
Real-time communication is a Blazor superpower:
// ChatHub.cs - SignalR Hub
using Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
private static readonly Dictionary<string, string> userConnections = new();
private static readonly List<ChatMessage> messageHistory = new();
public async Task SendMessage(string user, string message)
{
var chatMessage = new ChatMessage
{
User = user,
Message = message,
Timestamp = DateTime.Now
};
messageHistory.Add(chatMessage);
// Keep only last 100 messages
if (messageHistory.Count > 100)
{
messageHistory.RemoveAt(0);
}
await Clients.All.SendAsync("ReceiveMessage", chatMessage);
}
public async Task JoinChat(string userName)
{
userConnections[Context.ConnectionId] = userName;
await Clients.All.SendAsync("UserJoined", userName);
// Send message history to new user
await Clients.Caller.SendAsync("LoadMessageHistory", messageHistory);
}
public async Task LeaveChat()
{
if (userConnections.TryGetValue(Context.ConnectionId, out var userName))
{
userConnections.Remove(Context.ConnectionId);
await Clients.All.SendAsync("UserLeft", userName);
}
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
await LeaveChat();
await base.OnDisconnectedAsync(exception);
}
}
public class ChatMessage
{
public string User { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty;
public DateTime Timestamp { get; set; }
}

Join the conversation! Your thoughts help the community grow.