# Blazor ## Overview Blazor is a .NET web framework for building interactive client-side web UIs using C# and Razor syntax. It supports multiple hosting models: Blazor Server (SignalR-based), Blazor WebAssembly (client-side .NET runtime), and the unified model introduced in .NET 8 that combines static server-side rendering with per-component interactivity. Blazor components are reusable `.razor` files that encapsulate markup, logic, and state. The framework integrates natively with ASP.NET Core for authentication, dependency injection, and middleware. ## Component Fundamentals Blazor components are the building blocks of the UI. Each component is a `.razor` file with markup and C# code. ```csharp @page "/customers" @using Microsoft.AspNetCore.Components @using MyApp.Services @inject ICustomerService CustomerService @inject NavigationManager Navigation Customers

Customer Directory

@if (_customers is null) {

Loading customers...

} else if (_customers.Count == 0) {

No customers found.

} else { @foreach (var customer in _customers) { }
Name Email Joined
@customer.Name @customer.Email @customer.JoinedDate.ToShortDateString()
} @code { private List? _customers; protected override async Task OnInitializedAsync() { _customers = await CustomerService.GetAllAsync(); } private void EditCustomer(int id) { Navigation.NavigateTo($"/customers/{id}/edit"); } } ``` ## Render Modes (.NET 8+) The unified Blazor model in .NET 8+ lets you set render modes per-component: static SSR, interactive Server, interactive WebAssembly, or Auto. ```csharp // Program.cs - Configure render modes using Microsoft.AspNetCore.Components; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorComponents() .AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents(); builder.Services.AddScoped(); var app = builder.Build(); app.UseStaticFiles(); app.UseAntiforgery(); app.MapRazorComponents() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(MyApp.Client._Imports).Assembly); app.Run(); ``` Apply render modes to individual components: ```csharp @* Static SSR by default, interactive where needed *@ @page "/dashboard" @rendermode InteractiveServer

Dashboard

@code { private ChartData[] _chartData = Array.Empty(); protected override async Task OnInitializedAsync() { _chartData = await DashboardService.GetChartDataAsync(); } } ``` ## Reusable Component with Parameters and EventCallbacks Build composable components using parameters and event callbacks for parent-child communication. ```csharp @* Components/ConfirmDialog.razor *@ @namespace MyApp.Components @code { private bool _isVisible; [Parameter, EditorRequired] public string Title { get; set; } = string.Empty; [Parameter, EditorRequired] public string Message { get; set; } = string.Empty; [Parameter] public string ConfirmText { get; set; } = "Delete"; [Parameter] public EventCallback OnConfirmed { get; set; } [Parameter] public EventCallback OnCancelled { get; set; } public void Show() { _isVisible = true; StateHasChanged(); } public void Hide() { _isVisible = false; StateHasChanged(); } private async Task Confirm() { Hide(); await OnConfirmed.InvokeAsync(); } private async Task Cancel() { Hide(); await OnCancelled.InvokeAsync(); } } ``` ## Forms and Validation Blazor provides built-in form handling with `EditForm`, model binding, and `DataAnnotations` validation. ```csharp @page "/customers/create" @using System.ComponentModel.DataAnnotations @inject ICustomerService CustomerService @inject NavigationManager Navigation
@code { private CustomerFormModel _model = new(); private bool _isSubmitting; private async Task HandleSubmit() { _isSubmitting = true; try { await CustomerService.CreateAsync(_model); Navigation.NavigateTo("/customers"); } finally { _isSubmitting = false; } } public class CustomerFormModel { [Required, StringLength(100, MinimumLength = 2)] public string Name { get; set; } = string.Empty; [Required, EmailAddress] public string Email { get; set; } = string.Empty; [Required] public string Tier { get; set; } = string.Empty; } } ``` ## Hosting Model Comparison | Feature | Static SSR | Interactive Server | Interactive WASM | Auto | |---|---|---|---|---| | Initial load speed | Fastest | Fast | Slow (download runtime) | Fast then WASM | | Interactivity | None | Full | Full | Full | | Server connection | No | Required (SignalR) | No | Transitions off | | Offline capable | No | No | Yes | Eventually | | Server resource cost | Low | High (per-user circuit) | Low | Medium | | SEO friendly | Yes | Partial | No (without prerender) | Yes | ## Best Practices 1. **Add `@key` directives to every element inside `@foreach` loops** using a stable unique identifier (such as a database ID), not the loop index; without `@key`, Blazor's diffing algorithm reuses DOM elements incorrectly when items are inserted, removed, or reordered. 2. **Mark required component parameters with `[EditorRequired]`** so that consuming components get compile-time warnings when mandatory parameters are omitted, catching integration bugs before runtime. 3. **Avoid calling `StateHasChanged()` inside `OnInitializedAsync` or `OnParametersSetAsync`** because Blazor automatically re-renders after these lifecycle methods complete; redundant calls double the render work and cause visible flicker on Server render mode. 4. **Extract `@code` blocks exceeding 40 lines into a partial class code-behind file** (e.g., `MyComponent.razor.cs`) to keep markup readable, enable better IntelliSense, and allow the C# code to be unit-tested without Razor compilation. 5. **Use `CascadingValue` with `IsFixed="true"` for values that never change** (such as theme configuration or feature flags) so that Blazor skips change-detection on every render cycle for all descendant components that consume the value. 6. **Implement `IDisposable` on components that register event handlers, timers, or JS interop callbacks** and unsubscribe in `Dispose()`; Blazor Server circuits can leak memory for each connected user if handlers accumulate over the circuit lifetime. 7. **Wrap `IJSRuntime.InvokeAsync` calls in `OnAfterRenderAsync` guarded by `firstRender`** for DOM-dependent initialization (e.g., chart libraries, focus management); calling JS interop during `OnInitializedAsync` will throw during server-side prerendering because no DOM exists yet. 8. **Scope CSS to components using `MyComponent.razor.css` isolation files** and reference the generated `{Assembly}.styles.css` bundle in the layout rather than writing global CSS rules that collide across component libraries. 9. **Configure Blazor Server's `CircuitOptions.DetailedErrors` to `true` only in Development** and set `CircuitOptions.DisconnectedCircuitRetentionPeriod` to a bounded timespan (e.g., 3 minutes) in production to free server memory when users close browser tabs without disconnecting. 10. **Use `StreamRendering` attribute on pages that perform slow data fetches** so the initial HTML shell renders immediately with a loading placeholder and the content streams in as the async operation completes, improving perceived performance without requiring interactive mode.