What Blazor Is
Blazor builds web UI from C# components; each .razor file compiles to a class that produces a render tree.
protected override void BuildRenderTree(RenderTreeBuilder builder)Blazor is the part of ASP.NET Core that builds web pages out of C# instead of JavaScript. You write components. A component is one piece of a page: a button, a form, a whole screen. It lives in a file whose name ends in .razor, and that file holds HTML markup and C# together.
How a component becomes a page
The build step turns every .razor file into an ordinary C# class that inherits from ComponentBase. The markup becomes the body of a method named BuildRenderTree. The render tree is Blazor's own description of the HTML it wants: a flat list of elements, attributes and text. Nothing is written to the browser page directly.
When state changes, Blazor runs the component again, builds a new render tree, and compares it with the previous one. Only the differences are applied to the real page. That comparison is why one number on screen can change while the rest of the page stays untouched.
Where the code runs
The same component has two possible homes. It can run on the server, with clicks sent up a network connection and HTML patches sent back. Or the browser can download .NET itself, compiled to WebAssembly, and run the component locally. You choose per component. That choice is the subject of the render modes lesson.
What trips people up
A component with no render mode is rendered once on the server and then becomes plain HTML. The button appears, the click handler never runs, and nothing reports an error.
Example
* Components/Pages/Counter.razor *
"/counter"
InteractiveServer
<h1>Counter</h1>
<p>Current count: </p>
<button ="Increment">Click me</button>
{
// Ordinary C# field. The markup above reads it with @_count.
private int _count;
// Runs on a click. After it returns, Blazor rebuilds this component's
// render tree and sends only the changed text to the browser.
private void Increment()
{
_count++;
}
}
* Rendered HTML on first load:
<h1>Counter</h1>
<p>Current count: 0</p>
<button>Click me</button>
After one click, only the text inside the <p> changes to
"Current count: 1". The <h1> and the <button> are not re-sent. *When to use it
- Add a live-updating dashboard panel to an existing ASP.NET Core app without writing JavaScript.
- Share one validation class between the browser UI and the server API by keeping both in C#.
- Replace a page of hand-written jQuery with a component whose state is a plain C# field.
More examples
The same component written by hand, without Razor
Nobody writes components this way, but reading one explains the whole model: markup is a method call sequence, an event handler is an EventCallback, and the sequence numbers are what make the diff cheap. Reach for it only when you need to build markup from data at runtime, such as a component that renders a column set decided by configuration.
// Counter.cs - what the .razor file above compiles into.
// Razor is shorthand for exactly this; the two are the same thing.
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
namespace BlazorSample.Components.Pages;
[Route("/counter")] // this is what @page "/counter" generates
public class Counter : ComponentBase
{
private int _count;
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
// The int passed to every call is a sequence number. The Razor
// compiler assigns these; they tell the diff which call is which.
builder.OpenElement(0, "p");
builder.AddContent(1, $"Current count: {_count}");
builder.CloseElement();
builder.OpenElement(2, "button");
builder.AddAttribute(3, "onclick",
EventCallback.Factory.Create(this, Increment));
builder.AddContent(4, "Click me");
builder.CloseElement();
}
private void Increment()
{
_count++;
}
}The smallest Program.cs that serves components
This is the whole server side of a Blazor app. Browsing to /counter returns a full HTML document with the counter already rendered in it; the browser then opens a connection back for the click handling. Each render mode you want to use needs one call in the services block and one on MapRazorComponents.
// Program.cs of a Blazor Web App.
// App.razor is the root component; it contains the <html> document and
// renders <Routes /> for the page matching the current URL.
using BlazorSample.Components;
var builder = WebApplication.CreateBuilder(args);
// Services every Razor component needs, plus support for running
// components interactively on the server.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var app = builder.Build();
app.MapStaticAssets(); // .NET 9 and later; older templates used UseStaticFiles
app.UseAntiforgery(); // required before MapRazorComponents for form posts
// Finds every routable component in this assembly and serves it.
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();A component with no route, used inside another
Most components are not pages. A file without @page is used as a tag inside other components, and the file name becomes the tag name. This one is static markup plus two inputs, so it needs no render mode at all and costs nothing to render.
@* Components/Banner.razor - no @page, so no URL of its own *@
<div>
<strong>@Title</strong>
<p>@Body</p>
</div>
@code {
[Parameter]
public string Title { get; set; } = "";
[Parameter]
public string Body { get; set; } = "";
}
@* Components/Pages/Home.razor *@
@page "/"
<Banner Title="Maintenance" Body="Reports are rebuilt at 02:00 UTC." />
<Banner Title="Billing" Body="Invoices for March are ready." />
Discussion