Blazor lets you write full-stack web UIs in C# instead of JavaScript. It runs in the browser via WebAssembly or on the server with SignalR — ideal for .NET teams building SPAs without leaving the C# ecosystem.

Blazor Hosting Models

Model Runs where Best for
Blazor WebAssembly (WASM) Browser Offline-capable SPAs, CDN deployment
Blazor Server Server (SignalR) Fast initial load, internal apps
Blazor Hybrid .NET MAUI / WPF Desktop + mobile with web UI

This page focuses on Blazor WebAssembly and Blazor Server with ASP.NET Core.

Create a Blazor Project

  # WebAssembly standalone
dotnet new blazorwasm -n MyBlazorApp

# Server-side hosting
dotnet new blazor -n MyBlazorServer --interactivity Server

# Hosted WASM (API + client)
dotnet new blazorwasm -n MyHostedApp --hosted
cd MyHostedApp
dotnet run --project Server
  

Component Basics

Blazor UI is built from Razor components (.razor files):

  @page "/counter"

<h1>Counter</h1>
<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
  

Components are reusable building blocks with parameters, events, and lifecycle methods.

Data Binding

  <input @bind="searchText" @bind:event="oninput" placeholder="Search..." />
<p>You typed: @searchText</p>

<select @bind="selectedCategory">
    <option value="electronics">Electronics</option>
    <option value="books">Books</option>
</select>

@code {
    private string searchText = "";
    private string selectedCategory = "electronics";
}
  

Two-way binding syncs UI and state automatically. Use @bind:event="oninput" for real-time updates vs default onchange.

Parameters and Child Components

  <!-- ProductCard.razor -->
<div class="card">
    <h3>@Title</h3>
    <p>@Price.ToString("C")</p>
    <button @onclick="OnAddToCart">Add to Cart</button>
</div>

@code {
    [Parameter] public string Title { get; set; } = "";
    [Parameter] public decimal Price { get; set; }
    [Parameter] public EventCallback OnAddToCart { get; set; }
}
  
  <!-- Parent usage -->
<ProductCard Title="Laptop" Price="999.99m" OnAddToCart="HandleAdd" />
  

Fetching Data from APIs

  @page "/products"
@inject HttpClient Http

@if (products is null)
{
    <p>Loading...</p>
}
else
{
    <ul>
        @foreach (var p in products)
        {
            <li>@p.Name — @p.Price.ToString("C")</li>
        }
    </ul>
}

@code {
    private List<Product>? products;

    protected override async Task OnInitializedAsync()
    {
        products = await Http.GetFromJsonAsync<List<Product>>("api/products");
    }

    public record Product(int Id, string Name, decimal Price);
}
  

Register HttpClient in Program.cs with the API base address.

Forms and Validation

  <EditForm Model="@model" OnValidSubmit="HandleSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <InputText @bind-Value="model.Name" />
    <ValidationMessage For="@(() => model.Name)" />

    <InputNumber @bind-Value="model.Price" />
    <ValidationMessage For="@(() => model.Price)" />

    <button type="submit">Save</button>
</EditForm>

@code {
    private ProductModel model = new();

    private async Task HandleSubmit()
    {
        await Http.PostAsJsonAsync("api/products", model);
    }

    public class ProductModel
    {
        [Required, StringLength(100)]
        public string Name { get; set; } = "";

        [Range(0.01, 10000)]
        public decimal Price { get; set; }
    }
}
  

Component Lifecycle

Method When
OnInitialized / OnInitializedAsync Component created
OnParametersSet Parameters change
OnAfterRender DOM updated
Dispose Component removed

Use OnInitializedAsync for data loading; IDisposable for cleaning up timers and subscriptions.

Blazor Server Considerations

  • UI events travel over SignalR — latency matters for global users
  • Server holds circuit state per user — plan for scale-out with Azure SignalR Service
  • Not ideal for public internet apps with millions of users
  • Excellent for internal dashboards behind corporate VPN

Blazor WASM Considerations

  • Initial download includes .NET runtime (~2–3 MB compressed) — use lazy loading
  • Runs entirely in browser — API calls need CORS configured
  • Deploy static files to CDN; API separate
  • Debugging with browser DevTools + Visual Studio

Authentication

  // Program.cs
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthProvider>();
  
  <AuthorizeView>
    <Authorized>
        <p>Welcome, @context.User.Identity?.Name!</p>
    </Authorized>
    <NotAuthorized>
        <a href="/login">Log in</a>
    </NotAuthorized>
</AuthorizeView>
  

Integrate with ASP.NET Core Identity, Azure AD, or Auth0.

JavaScript Interop

Call JS from C# when needed:

  @inject IJSRuntime JS

await JS.InvokeVoidAsync("console.log", "Hello from Blazor");
var width = await JS.InvokeAsync<int>("eval", "window.innerWidth");
  

Use sparingly — prefer pure Blazor solutions.

Production Checklist

  • Choose hosting model based on audience and latency
  • Lazy-load large assemblies in WASM
  • Configure CORS for API access
  • Error boundaries (<ErrorBoundary>) for graceful failures
  • AOT compilation for WASM performance (.NET 8+)
  • Pre-render critical pages for SEO (Blazor SSR in .NET 8)

Blazor brings .NET’s type safety and tooling to the frontend — a compelling choice for teams already invested in the Microsoft ecosystem.