A “Hello World” in Blazor is the minimal example to verify the complete page lifecycle, from the request to the HTML displayed in the browser.
In the previous article, we set up the environment and created the solution. Now we’ll press Run (or F5) and, most importantly, understand why it works.
Let’s walk through the modern Blazor template from the moment the browser makes the request until we see the text on screen.
The Goal: Home.razor
When you open the browser at https://localhost:xxxx/, what you’re seeing is the rendered output of the Home.razor component.
You’ll find it in the Components/Pages/Home.razor folder.
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.This is a very simple file, but it has crucial details:
- Directive
@page "/": This defines the route. It tells Blazor: “When the user requests the root of the site, load me.” - Standard HTML: The rest (
h1, text) is regular HTML. - Built-in Components:
<PageTitle>is a Blazor component that allows us to dynamically modify the browser tab’s title.
So, how does Blazor know to display this file? To answer that, we need to go to the very beginning.
The Entry Point: Program.cs
Like any .NET console application (yes, ASP.NET Core is a souped-up console app), everything starts in Program.cs.
This is where the web server and services are configured. Let’s look at the key lines for Blazor:
var builder = WebApplication.CreateBuilder(args);
// 1. SERVICE REGISTRATION
// Adds the necessary services for rendering Razor components
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents() // Enables Server mode (SignalR)
.AddInteractiveWebAssemblyComponents(); // Enables WebAssembly mode
var app = builder.Build();
// ... HTTPS and static files configuration ...
app.UseAntiforgery(); // CSRF security
// 2. COMPONENT MAPPING
// This tells Blazor: "The root component of the entire app is 'App'"
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(Counter).Assembly); // Connects to the Client project
app.Run();Notice app.MapRazorComponents<App>(). This line connects the HTTP request with Blazor’s component system, designating App.razor as the parent of all components.
The HTML Document: App.razor
The Components/App.razor file defines the base HTML document structure and hosts the routing component.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="BlazorApp1.styles.css" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</body>
</html>Three important elements here:
<base href="/" />: Essential. It tells the browser how to resolve relative paths. Without this, Blazor navigation won’t work.<Routes />: Blazor injects the dynamic content at this point. It’s a placeholder indicating where the user-requested page will go.blazor.web.js: This script starts the framework in the browser. It handles connecting to the server (if Server mode) or loading the runtime (if WASM).
Do you see the BlazorApp1.styles.css file? It’s a virtual file automatically generated by Blazor for CSS Isolation (the encapsulated styles for each component).
The Router: Routes.razor
The <Routes /> component we saw above is defined in Routes.razor. It’s the brain of navigation.
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>Its logic is straightforward:
- It scans all components in the assembly (
AppAssembly). - It looks for those with the
@pagedirective. - It compares the browser’s URL with the component’s route.
- If it finds a match (
Found): It renders the component using theDefaultLayout(which isMainLayout). - If it does NOT find a match (
NotFound): It displays the 404 error message.
The Common Layout: MainLayout.razor
Finally, before rendering Home.razor, Blazor wraps it in MainLayout.razor. This component defines the common structure of the website (sidebar, top menu, footer).
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>The key point here is @Body.
This exact position is where our Home.razor will be rendered.
Execution Flow
To have a clear mental map, here is what happens when you press Enter in the address bar:
Request: The browser requests https://yourdomain/.
Server (Program.cs): ASP.NET Core receives the request and passes it to the Blazor middleware.
Root (App.razor): Starts generating the HTML. The <Routes /> component is encountered.
Router: The Router sees the URL is / and looks for a component with @page "/". It finds Home.razor.
Layout: The Router sees it must use MainLayout. It instantiates the Layout.
Rendering: Inside the Layout’s @Body, Home.razor is instantiated.
Response: The server returns the complete HTML to the browser (SSR).
Interactivity: If the page uses an interactive mode, blazor.web.js starts the corresponding runtime and connects the event handlers. In static SSR, the page remains without Blazor interactivity.