Blazor in C#: WebAssembly vs Server for Interactive Web UIs
1. Blazor and WebAssembly Overview
Q: What is Blazor in C#?
Blazor is a .NET framework for building interactive web UIs using C# instead of JavaScript. It allows developers to create single-page applications (SPAs) with rich client-side or server-side interactivity. Blazor has two hosting models:
- Blazor Server: Runs on the server, uses SignalR for real-time UI updates.
- Blazor WebAssembly: Runs in the browser, using .NET compiled to WebAssembly for client-side execution.
Q: What is WebAssembly in the context of Blazor?
WebAssembly (WASM) is a binary instruction format that runs in browsers, enabling high-performance execution of languages like C# in the client’s browser. Blazor WebAssembly compiles .NET code to WASM, allowing C# to run client-side without plugins, interacting with the DOM via a lightweight runtime.
Q: Why are Blazor and WebAssembly important?
- Enables C# for both client and server-side web development, reducing reliance on JavaScript.
- Provides type-safe, managed code with .NET tooling (e.g., Visual Studio).
- Blazor Server offers low-latency updates via SignalR; WebAssembly enables offline-capable apps.
- Supports reusable components, data binding, and modern web features.
Q: How does Blazor differ from C/C++ for web development?
- Blazor: Managed, uses C# and .NET, runs on WebAssembly or server-side with SignalR, integrates with ASP.NET Core for routing and DI.
- C/C++: Requires manual WebAssembly compilation (e.g., via Emscripten) or JavaScript bindings, no native web framework, and complex DOM manipulation.
- C# Advantage: Simplified development, type safety, rich component model, and Visual Studio integration.
2. Creating Interactive Web UIs with Blazor
Q: How do you create interactive web UIs with Blazor?
In Blazor, interactive UIs are built using:
- Components: Reusable UI elements defined as
.razorfiles, combining C# and HTML-like Razor syntax. - Data Binding: Connects component properties to UI elements (e.g., one-way, two-way binding).
- Event Handling: Uses C# event handlers (e.g.,
@onclick) for user interactions (clicks, input changes). - Dependency Injection: Injects services (e.g., HTTP clients) into components.
- Routing: Maps URLs to components using
@pagedirectives. - Blazor supports both server-side (real-time updates via SignalR) and client-side (WebAssembly) models.
Q: What are the key features of Blazor components?
- Defined in
.razorfiles with C# and Razor markup. - Support parameters (
[Parameter]) for passing data. - Use lifecycle methods (e.g.,
OnInitialized,OnParametersSet) for initialization. - Enable event handling with directives (e.g.,
@onclick,@onchange). - Support data binding for dynamic UI updates.
Q: Can you give an example of creating an interactive web UI with Blazor WebAssembly?
Below is an example of a Blazor WebAssembly application with a simple interactive UI for managing a list of tasks.
@page "/todo"
@using System.Collections.Generic
@inject ITaskService TaskService
<h3>Todo List</h3>
<div>
<input @bind="newTask" placeholder="Enter new task" />
<button @onclick="AddTask">Add Task</button>
</div>
<ul>
@foreach (var task in TaskService.GetTasks())
{
<li>
@task.Title
<button @onclick="() => RemoveTask(task.Id)">Remove</button>
</li>
}
</ul>
@code {
private string newTask = string.Empty;
private void AddTask()
{
if (!string.IsNullOrWhiteSpace(newTask))
{
TaskService.AddTask(new TaskItem { Title = newTask });
newTask = string.Empty;
}
}
private void RemoveTask(int id)
{
TaskService.RemoveTask(id);
}
}
@* Model and Service (injected) *@
@code {
public class TaskItem
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
}
public interface ITaskService
{
List<TaskItem> GetTasks();
void AddTask(TaskItem task);
void RemoveTask(int id);
}
}
namespace BlazorTodo
{
public class TaskService : ITaskService
{
private readonly List<TaskItem> _tasks = new();
public List<TaskItem> GetTasks() => _tasks;
public void AddTask(TaskItem task)
{
task.Id = _tasks.Count + 1;
_tasks.Add(task);
}
public void RemoveTask(int id)
{
var task = _tasks.FirstOrDefault(t => t.Id == id);
if (task != null)
{
_tasks.Remove(task);
}
}
}
}
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using BlazorTodo;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddSingleton<ITaskService, TaskService>();
await builder.Build().RunAsync();
Description: A Blazor WebAssembly app with a Todo component that allows users to add and remove tasks. Features:
- Routing:
@page "/todo"maps the component to/todo. - Data Binding:
@bindlinks the input tonewTask. - Event Handling:
@onclickhandles button clicks. - Dependency Injection:
ITaskServiceis injected for task management.
Usage:
- Navigate to
/todo, enter a task, and click "Add Task" to add it to the list. - Click "Remove" to delete a task.
3. Blazor Server vs WebAssembly + Interactivity
Q: What are the differences between Blazor Server and Blazor WebAssembly?
Blazor Server:
- Runs on the server, uses SignalR for real-time UI updates.
- Minimal client-side code, faster initial load.
- Requires constant server connection, higher latency for remote clients.
- Better for intranet apps or low-latency scenarios.
Blazor WebAssembly:
- Runs in the browser, compiled to WebAssembly.
- Offline-capable, no server connection needed after loading.
- Larger initial download, client-side performance depends on device.
- Better for public-facing SPAs or offline apps.
Q: How do you implement interactivity in Blazor?
Interactivity is achieved through:
- Event Handling: Use directives like
@onclick,@onchange, or@onkeydownto bind C# methods to UI events. - Data Binding: Use
@bindfor two-way binding or@bind-valuefor specific properties. - Component Lifecycle: Use methods like
OnInitializedAsync,OnParametersSetAsyncfor dynamic updates. - State Management: Use services (injected via DI) or state containers for shared state.
- JavaScript Interop: Call JavaScript from C# or vice versa for advanced browser APIs.
Q: Can you give an example of a Blazor Server app with interactivity?
@page "/todo"
@using System.Collections.Generic
@inject ITaskService TaskService
<h3>Todo List (Blazor Server)</h3>
<div>
<input @bind="newTask" @bind:event="oninput" placeholder="Enter new task" />
<button @onclick="AddTask">Add Task</button>
</div>
<ul>
@foreach (var task in TaskService.GetTasks())
{
<li>
@task.Title
<button @onclick="() => RemoveTask(task.Id)">Remove</button>
</li>
}
</ul>
<p>Current input: @newTask</p>
@code {
private string newTask = string.Empty;
private void AddTask()
{
if (!string.IsNullOrWhiteSpace(newTask))
{
TaskService.AddTask(new TaskItem { Title = newTask });
newTask = string.Empty;
}
}
private void RemoveTask(int id)
{
TaskService.RemoveTask(id);
}
}
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using BlazorTodo;
var builder = WebApplication.CreateBuilder(args);
// Add Blazor Server services
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<ITaskService, TaskService>();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
@page "/"
@namespace BlazorTodo
@using Microsoft.AspNetCore.Components.Web
<!DOCTYPE html>
<html>
<head>
<title>Blazor Server Todo</title>
<base href="/" />
<component type="typeof(App)" render-mode="ServerPrerendered" />
</head>
<body>
<div id="app"></div>
<script src="_framework/blazor.server.js"></script>
</body>
</html>
Description: A Blazor Server app with a Todo component, similar to the WebAssembly example, but using server-side rendering with SignalR. Features:
- Interactivity: Real-time input binding (
@bind:event="oninput") and button clicks. - Routing:
@page "/todo"for navigation. - DI: Injects
ITaskService(same as WebAssembly example).
Usage:
- Navigate to
/todo, enter a task, and click "Add Task" or remove tasks. The UI updates in real-time via SignalR.
4. Common Mistakes & Best Practices
Q: Common mistakes?
General:
- Choosing the wrong hosting model (e.g., Blazor Server for offline apps).
- Overloading components with logic instead of using services.
- Not handling JavaScript interop correctly, causing errors.
Blazor WebAssembly:
- Ignoring initial download size, impacting performance.
- Not optimizing client-side resources for browser constraints.
Blazor Server:
- Not accounting for SignalR connection issues or latency.
- Overusing server-side state, causing scalability issues.
Q: Best practices?
General:
- Use components for reusable, modular UI elements.
- Follow MVVM or service-based patterns to separate logic from UI.
- Use dependency injection for services (e.g., HTTP clients, state management).
- Validate inputs in event handlers to prevent errors.
Blazor WebAssembly:
- Optimize app size with AOT compilation or trimming.
- Use lazy loading for large assemblies to improve load time.
- Handle browser APIs via JavaScript interop when needed.
Blazor Server:
- Minimize server-side state to improve scalability.
- Handle connection disruptions with circuit handlers.
- Use prerendering to improve initial load performance.
Interactivity:
- Use
@bindfor simple two-way binding, avoid overcomplicating with custom events. - Keep event handlers short, delegating to services for complex logic.
- Use lifecycle methods (
OnInitializedAsync) for initialization.
General Practices:
- Test components with unit tests (e.g., bUnit for Blazor).
- Document components with XML comments or Razor comments.
- Use CSS isolation in
.razor.cssfiles for scoped styles. - Leverage modern C# features (e.g., nullable types, pattern matching).