Blazor WebAssembly

The Blazor WebAssembly project template is used to create a stand-alone Blazor WebAssembly application. In this lesson, we will look at its structure. Let’s look at the complete project setup below:

<Router AppAssembly="@typeof(App).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>
Blazor WebAssembly project

The complete project structure is demonstrated in the following diagram:

Blazor WebAssembly project file tree
Blazor WebAssembly project file tree

Project file structure and core libraries

In the above example, our project folder is called Blazor, and the project file is Blazor.csproj. It has the following content:

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0" PrivateAssets="all" />
</ItemGroup>
</Project>

A Blazor WebAssembly project file is based on the Microsoft.NET.Sdk.BlazorWebAssembly SDK. This is defined inside the opening Project tag on line 1.

Every Blazor WebAssembly project relies on the Microsoft.AspNetCore.Components.WebAssembly NuGet package, which we have on line 10 in the project file. We also have a reference to Microsoft.AspNetCore.Components.WebAssembly.DevServer NuGet package on line 11. This package enables us to launch and host the application in the development environment.

Application entry point

As with most .NET applications, a Blazor WebAssembly application has the Program.cs file as its entry point. In the example above, this file has the following content.

using Blazor;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();

On line 5, we create a default Blazor WebAssembly host builder. On line 6, we determine which class will represent our application. In the example above, this class is called App. We associate this class with an HTML element that has the id attribute app. On line 7, we define the template for the header, which is represented by the HeadOutlet class and is mapped to the head::after CSS selector. On line 9, we are adding a scoped reference to a HttpClient instance. Finally, on line 11, we are building and running our application.

Other files in the project root folder

The remaining files in the root folder of our project have a .razor extension. This is the file extension used by individual Blazor units, such as pages, layouts, and reference files. These are collectively called Razor components. These files are _Imports.razor and App.razor.

The _Imports.razor file contains shared namespaces used by other files with .razor extensions inside the application. If we define any specific namespace in this file, we don't need to explicitly define them in any other Razor components.

The App.razor file is the container object for our application and its user interfaces. In the example above, this file has the following structure.

<Router AppAssembly="@typeof(App).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>

Router is the root element defined by the markup. If we point at a path that maps to a valid Razor page, the content inside the Found element will be used that is defined on lines 2–5. The RouteView element on line 3 represents the placeholder for the layout, which will then contain the actual page.

If there is no Razor page associated with the URL path provided, the NotFound element will be used, which is defined on lines 6–11. We still use a layout defined by the LayoutView element on lines 8–10. Instead of populating it with a Razor page, we populate it with a message telling the user that the page has not been found.

Shared components

The Shared folder contains Razor components that are shared by the application. Examples of these shared resources include layouts and navigational menus. For example, let’s examine the MainLayout.razor file, which has the following content:

@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<article class="content px-4">
@Body
</article>
</main>
</div>

In Blazor, a layout component inherits from the LayoutComponentBase class, as we have defined on line 1. It can then have a combination of HTML and Razor components. The NavMenu reference on line 5 is an example of a Razor component reference. The body of the Razor page gets inserted into the @Body placeholder directive that we have on line 10.

Individual pages

Individual pages are located inside the Pages folder of the project. When a specific page is requested, a layout is displayed with the content of this page inside of it.

Static resources

Static recourses, such as HTML, CSS, and JavaScript files, are inside the wwwroot folder. The Blazor application is hosted inside an HTML page. By default, we have the index.html file for this purpose. The default structure of this file is as follows:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>BlazorWebAssemblyExample</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link rel="icon" type="image/png" href="favicon.png" />
<link href="BlazorWebAssemblyExample.styles.css" rel="stylesheet" />
</head>
<body>
<div id="app">
<svg class="loading-progress">
<circle r="40%" cx="50%" cy="50%" />
<circle r="40%" cx="50%" cy="50%" />
</svg>
<div class="loading-progress-text"></div>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>

The Blazor application is inserted into the div element with the id attribute of app, as we have defined on line 16. This is the element we pointed to when we configured our application in the Program.cs file.