C# Blazor Basics
Introduction
Blazor is a modern web framework developed by Microsoft that allows developers to build interactive web UIs using C# instead of JavaScript. It's a significant shift in web development because it enables C# developers to leverage their existing skills to create full-stack web applications with a unified language across both server and client.
Blazor comes in two hosting models:
- Blazor Server - Runs your C# code on the server with UI updates sent over a SignalR connection
- Blazor WebAssembly - Runs your C# code directly in the browser using WebAssembly
In this tutorial, we'll cover the fundamental concepts of Blazor, how to create components, handle data binding, and build practical interactive web applications.
Getting Started with Blazor
Prerequisites
Before we begin, make sure you have the following installed:
- .NET 6.0 SDK or newer
- A code editor (Visual Studio, Visual Studio Code with C# extension, or JetBrains Rider)
Creating Your First Blazor App
Let's create a new Blazor Server application using the .NET CLI:
dotnet new blazorserver -o MyFirstBlazorApp
cd MyFirstBlazorApp
dotnet run
This will create and run a Blazor Server application. Navigate to https://localhost:5001
in your browser to see your application running.
Understanding Blazor Components
Components are the building blocks of Blazor applications. A component is a self-contained piece of UI that includes HTML markup and C# code.
Basic Component Structure
Here's a simple Blazor component:
@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++;
}
}
Let's break down what's happening here:
@page "/counter"
defines the route for this component- HTML markup defines the UI structure
@currentCount
displays the current value using data binding@onclick="IncrementCount"
binds the button's click event to a C# method- The
@code
block contains C# logic for the component
Creating a Custom Component
Let's create a simple greeting component:
@* Greeting.razor *@
<div class="alert alert-info">
<h3>Hello, @Name!</h3>
<p>Welcome to Blazor.</p>
</div>
@code {
[Parameter]
public string Name { get; set; } = "Friend";
}
To use this component in another component:
@page "/welcome"
<h1>Welcome Page</h1>
<Greeting Name="John" />
<Greeting Name="Sarah" />
<Greeting /> @* Will use the default value "Friend" *@
Data Binding in Blazor
Blazor provides powerful data binding capabilities that link your UI elements with your C# code.
One-way Data Binding
One-way data binding displays data from your C# properties in the UI:
<h3>Current Temperature: @temperature °C</h3>
@code {
private int temperature = 25;
}
Two-way Data Binding
Two-way data binding allows changes in the UI to update your C# model and vice versa:
<input @bind="username" />
<p>Hello, @username!</p>
@code {
private string username = "";
}
For real-time updates as the user types, use @bind-value
and @bind-value:event
:
<input @bind-value="searchText" @bind-value:event="oninput" />
<p>You are searching for: @searchText</p>
@code {
private string searchText = "";
}
Handling Events
Blazor allows you to handle DOM events using C# methods:
<button @onclick="HandleClick">Click Me</button>
<p>Button clicked @clickCount times.</p>
@code {
private int clickCount = 0;
private void HandleClick()
{
clickCount++;
}
}
You can also pass parameters to event handlers:
<button @onclick="() => IncrementBy(5)">Add 5</button>
<button @onclick="() => IncrementBy(10)">Add 10</button>
<p>Count: @count</p>
@code {
private int count = 0;
private void IncrementBy(int value)
{
count += value;
}
}
Component Lifecycle
Blazor components have lifecycle methods that you can override to execute code at specific points:
@page "/lifecycle"
@implements IDisposable
<h1>Component Lifecycle Demo</h1>
<p>Current count: @count</p>
@code {
private int count = 0;
private System.Threading.Timer? timer;
// Called when the component is initialized
protected override void OnInitialized()
{
Console.WriteLine("OnInitialized called");
}
// Called after the component has been rendered to the DOM
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
Console.WriteLine("First render complete");
timer = new System.Threading.Timer(_ =>
{
count++;
InvokeAsync(StateHasChanged); // Update UI
}, null, 1000, 1000);
}
}
// Called when the component is disposed
public void Dispose()
{
Console.WriteLine("Component being disposed");
timer?.Dispose();
}
}
Practical Example: Todo List Application
Let's build a simple todo list application to demonstrate these concepts:
@page "/todo"
<h3>Todo List</h3>
<div class="form-group">
<input placeholder="What needs to be done?" @bind="newTodo" @bind:event="oninput" @onkeyup="AddTodoOnEnter" class="form-control" />
<button class="btn btn-primary mt-2" @onclick="AddTodo" disabled="@string.IsNullOrWhiteSpace(newTodo)">Add Todo</button>
</div>
@if (todos.Count == 0)
{
<p class="mt-3">No todos yet! Add some.</p>
}
else
{
<ul class="mt-3 list-group">
@foreach (var todo in todos)
{
<li class="list-group-item">
<input type="checkbox" @bind="todo.IsDone" />
<span style="@(todo.IsDone ? "text-decoration: line-through" : "")">@todo.Title</span>
<button class="btn btn-sm btn-danger float-end" @onclick="() => RemoveTodo(todo)">Delete</button>
</li>
}
</ul>
<p class="mt-3">@todos.Count(t => !t.IsDone) out of @todos.Count items remaining</p>
@if (todos.Any(t => t.IsDone))
{
<button class="btn btn-sm btn-warning" @onclick="RemoveCompletedTodos">Clear Completed</button>
}
}
@code {
private List<TodoItem> todos = new List<TodoItem>();
private string newTodo = "";
private void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = "";
}
}
private void AddTodoOnEnter(KeyboardEventArgs e)
{
if (e.Key == "Enter")
{
AddTodo();
}
}
private void RemoveTodo(TodoItem todo)
{
todos.Remove(todo);
}
private void RemoveCompletedTodos()
{
todos.RemoveAll(t => t.IsDone);
}
private class TodoItem
{
public string Title { get; set; } = "";
public bool IsDone { get; set; }
}
}
This example showcases:
- Two-way data binding with
@bind
- Event handling with
@onclick
and@onkeyup
- Conditional rendering with
@if
statements - List rendering with
@foreach
- CSS styling based on component state
- State management with C# properties and methods
Dependency Injection
Blazor fully supports dependency injection. Here's how to use services:
@page "/weather"
@inject WeatherService WeatherService
<h1>Weather Forecast</h1>
@if (forecasts == null)
{
<p>Loading...</p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await WeatherService.GetForecastAsync();
}
}
To register this service, add it to the Program.cs
file:
builder.Services.AddSingleton<WeatherService>();
Summary
In this tutorial, you've learned the basics of Blazor, including:
- Setting up a Blazor application
- Creating and using components
- Implementing data binding
- Handling events
- Working with component lifecycle
- Building a practical todo application
- Using dependency injection
Blazor brings the power of C# to web development, allowing you to build rich, interactive web applications without having to write JavaScript. It's particularly appealing for C# developers who want to leverage their existing skills for full-stack development.
Additional Resources
- Official Blazor Documentation
- Blazor University - In-depth learning resources
- Awesome Blazor - Curated list of resources
Exercises
- Enhance the todo application to persist todos in browser localStorage
- Create a weather dashboard that fetches data from a weather API
- Build a simple calculator application with Blazor
- Create a form with validation using
EditForm
andDataAnnotations
- Implement a component that uses JavaScript interop to access browser APIs
Happy coding with Blazor!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)