.NET Blazor
Introduction
Blazor is a modern web framework developed by Microsoft that enables developers to build interactive web applications using C# and .NET instead of JavaScript. This unique approach allows .NET developers to leverage their existing skills to create robust, client-side web applications without needing to learn JavaScript frameworks like React, Angular, or Vue.
Blazor comes in two primary hosting models:
- Blazor WebAssembly (Client-side): Runs entirely in the browser using WebAssembly
- Blazor Server: Runs on the server within an ASP.NET Core application
In this guide, we'll explore what makes Blazor powerful, how it works, and how to build your first Blazor application.
Understanding Blazor Architecture
How Blazor Works
Blazor uses a component-based architecture similar to modern JavaScript frameworks. Each UI element in a Blazor application is encapsulated in a component that:
- Contains HTML markup
- Contains C# code for handling events and managing state
- Can be nested, reused, and shared across projects
Blazor WebAssembly
Blazor WebAssembly runs entirely in the browser via WebAssembly (WASM), a binary instruction format that enables high-performance execution of code in web browsers. Here's the runtime flow:
- The browser downloads a small .NET runtime along with your application's .NET assemblies
- The .NET runtime boots within WebAssembly in the browser
- Your application runs directly in the browser
Blazor Server
Blazor Server keeps the application logic on the server and uses SignalR (a real-time messaging library) to communicate with the client:
- The server maintains a circuit for each client connection
- UI updates are sent from server to client via SignalR
- User events are sent from client to server for processing
Setting Up Your First Blazor Application
Let's create a basic Blazor application to see how easy it is to get started.
Prerequisites
- Install the latest .NET SDK (6.0 or later recommended)
- A code editor (Visual Studio, VS Code with C# extension, or JetBrains Rider)
Creating a Blazor WebAssembly Project
Open a command prompt and run:
dotnet new blazorwasm -o MyFirstBlazorApp
cd MyFirstBlazorApp
dotnet run
This creates a new Blazor WebAssembly project and launches it. Navigate to https://localhost:5001
in your browser to see your application running.
Blazor Components: The Building Blocks
Blazor applications are built using components. A component is a self-contained piece of user interface with its own state and behavior.
Anatomy of a Component
Here's a simple counter component:
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
In this example:
@page "/counter"
defines the route for this component- The HTML markup describes the UI
@currentCount
displays a C# variable in the UI@onclick="IncrementCount"
binds a button click to a C# method- The
@code
block contains C# code that manages component state and behavior
Component Lifecycle
Blazor components have lifecycle methods that you can override to run code at specific times:
@code {
protected override void OnInitialized()
{
// Called when the component is initialized
Console.WriteLine("Component initialized");
}
protected override async Task OnInitializedAsync()
{
// Async version - great for loading data
await LoadDataAsync();
}
protected override void OnParametersSet()
{
// Called when parameters are set
}
protected override void OnAfterRender(bool firstRender)
{
// Called after the component has rendered
if (firstRender)
{
// This is the first time the component has rendered
}
}
}
Data Binding in Blazor
Blazor provides powerful data binding capabilities to synchronize data between UI elements and component state.
One-way Binding
One-way binding displays a value in the UI but doesn't update the source when the UI changes:
<p>The current count is: @currentCount</p>
Two-way Binding
Two-way binding keeps UI elements and C# properties in sync:
<input @bind="username" />
<p>Hello, @username!</p>
@code {
private string username = "Guest";
}
The @bind
directive creates a two-way binding between the input element and the username
variable. Changes to the input update the variable, and vice versa.
Event Binding
You can bind DOM events to C# methods:
<button @onclick="HandleClick">Click Me</button>
<button @onclick="() => HandleClickWithParam('Hello')">Click With Param</button>
@code {
private void HandleClick()
{
Console.WriteLine("Button clicked!");
}
private void HandleClickWithParam(string message)
{
Console.WriteLine($"Button clicked with message: {message}");
}
}
Forms and Validation
Blazor integrates with ASP.NET Core's form validation system for client-side validation.
Building a Simple Form
@page "/registration"
@using System.ComponentModel.DataAnnotations
<h3>Registration Form</h3>
<EditForm Model="@registrationModel" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group">
<label for="email">Email:</label>
<InputText id="email" @bind-Value="registrationModel.Email" class="form-control" />
<ValidationMessage For="@(() => registrationModel.Email)" />
</div>
<div class="form-group">
<label for="password">Password:</label>
<InputText type="password" id="password" @bind-Value="registrationModel.Password" class="form-control" />
<ValidationMessage For="@(() => registrationModel.Password)" />
</div>
<button type="submit" class="btn btn-primary">Register</button>
</EditForm>
@code {
private RegistrationModel registrationModel = new RegistrationModel();
private void HandleValidSubmit()
{
Console.WriteLine("Form submitted successfully!");
// Process the valid form
}
public class RegistrationModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "Password must be at least {2} characters long.", MinimumLength = 6)]
public string Password { get; set; }
}
}
This form uses:
EditForm
to wrap the formDataAnnotationsValidator
to enable validationInputText
components that support two-way binding and validationValidationMessage
to display field-specific errorsValidationSummary
to display all validation errors
Communication Between Components
Parameters
Pass data to a child component using parameters:
<!-- Parent Component -->
<ChildComponent Title="Hello from parent" Count="42" />
<!-- Child Component (ChildComponent.razor) -->
@code {
[Parameter]
public string Title { get; set; }
[Parameter]
public int Count { get; set; }
}
<h2>@Title</h2>
<p>The count is: @Count</p>
Event Callbacks
Child components can communicate back to parents using event callbacks:
<!-- Child Component (ChildComponent.razor) -->
<button @onclick="OnButtonClicked">Click Me</button>
@code {
[Parameter]
public EventCallback<string> OnClick { get; set; }
private async Task OnButtonClicked()
{
await OnClick.InvokeAsync("Button was clicked!");
}
}
<!-- Parent Component -->
<ChildComponent OnClick="HandleChildClick" />
@code {
private void HandleChildClick(string message)
{
Console.WriteLine($"Message from child: {message}");
}
}
Practical Example: Building a Todo Application
Let's build a simple todo application to demonstrate Blazor's capabilities:
@page "/todos"
<h3>Todo List</h3>
<div class="input-group mb-3">
<input @bind="newTodo" @bind:event="oninput" @onkeypress="HandleKeyPress" class="form-control" placeholder="What needs to be done?" />
<div class="input-group-append">
<button class="btn btn-primary" @onclick="AddTodo">Add</button>
</div>
</div>
<ul class="list-group mt-3">
@foreach (var todo in todos)
{
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
<input type="checkbox" @bind="todo.IsDone" />
<span style="@(todo.IsDone ? "text-decoration:line-through" : "")">@todo.Title</span>
</div>
<button class="btn btn-sm btn-danger" @onclick="() => RemoveTodo(todo)">Delete</button>
</li>
}
</ul>
@if (todos.Count > 0)
{
<div class="mt-3">
<button class="btn btn-secondary" @onclick="() => todos.Clear()">Clear All</button>
</div>
}
@code {
private string newTodo = "";
private List<TodoItem> todos = new List<TodoItem>();
private void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = "";
}
}
private void RemoveTodo(TodoItem todo)
{
todos.Remove(todo);
}
private void HandleKeyPress(KeyboardEventArgs e)
{
if (e.Key == "Enter")
{
AddTodo();
}
}
private class TodoItem
{
public string Title { get; set; }
public bool IsDone { get; set; }
}
}
This example demonstrates:
- Two-way binding with input fields
- Event handling for button clicks and keyboard events
- Conditional styling based on state
- Dynamic lists with the
@foreach
directive - Adding and removing items from collections
- Component state management
Working with APIs and Data
Blazor applications often need to fetch data from APIs. Here's how to do it:
@page "/fetchdata"
@using System.Net.Http
@using System.Net.Http.Json
@inject HttpClient Http
<h1>Weather forecast</h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
try
{
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
}
catch (Exception ex)
{
Console.WriteLine($"Error loading data: {ex.Message}");
}
}
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}
In this example:
- We inject
HttpClient
to make HTTP requests - Use
GetFromJsonAsync
to fetch and deserialize JSON data - Display loading state while data is being fetched
- Handle exceptions that might occur during data fetching
Blazor vs. Other Web Frameworks
Advantages of Blazor
- C# Everywhere: Use C# for both client and server code
- Shared Code: Share models, validation, and business logic between client and server
- Rich .NET Ecosystem: Access to the vast .NET library ecosystem
- No JavaScript Required: Build complete applications with minimal to no JavaScript
- Server and WebAssembly Options: Choose the hosting model that fits your needs
Considerations
- WebAssembly Size: Initial download can be larger (though this is improving)
- Maturity: Newer than established frameworks like React or Angular
- Browser Support: WebAssembly is supported in all modern browsers, but older browsers may not be compatible
Deployment Options
Deploying Blazor WebAssembly
Blazor WebAssembly apps are static files that can be deployed to:
- Static web hosting services (Azure Static Web Apps, GitHub Pages, etc.)
- CDN services
- Traditional web servers (IIS, Apache, Nginx)
Example deployment command:
dotnet publish -c Release
This creates a publish
folder with static files you can deploy to any web server.
Deploying Blazor Server
Blazor Server apps are ASP.NET Core applications that can be deployed to:
- Azure App Service
- IIS
- Docker containers
- Linux servers with Nginx/Apache
Summary
.NET Blazor represents a revolutionary approach to web development by allowing C# developers to build interactive web UIs without writing JavaScript. Key takeaways include:
- Blazor offers two hosting models: WebAssembly (client-side) and Server
- Components are the building blocks of Blazor applications
- Blazor provides robust data binding, forms, and validation capabilities
- Communication between components is handled via parameters and event callbacks
- Blazor integrates well with RESTful APIs and other data sources
Whether you're building internal business applications, public-facing websites, or progressive web apps, Blazor provides a powerful platform for .NET developers to create modern web experiences.
Additional Resources
- Official Blazor Documentation
- Blazor University - In-depth tutorials
- Awesome Blazor - A collection of Blazor resources
- BlazorSchool.com - Free tutorials and courses
Exercises
- Create a Blazor counter component that can increment and decrement
- Build a simple calculator with basic operations (+, -, *, /)
- Create a weather dashboard that fetches data from a public weather API
- Implement a master-detail view with a list of products and product details
- Build a form with validation for user registration
By completing these exercises, you'll gain practical experience with the core concepts of Blazor development and be well on your way to building sophisticated web applications with .NET!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)