Not so long ago Microsoft released .NET 5.0 and with this release, their front-end framework Blazor is getting even better support for Web Assembly hosting solution.
In this article, we will try to write a TODO list SPA Web Assembly application using this framework.
But before diving in let’s talk a little bit about what kind of benefits and downsides Web Assembly hosting solution carry with it.
WebAssembly (often shortened to Wasm) is an open standard that defines a portable binary code format for executable programs, and a corresponding textual assembly language, as well as interfaces for facilitating interactions between such programs and their host environment. — Wikipedia
The Blazor WebAssembly hosting model offers several benefits:
- There’s no .NET server-side dependency. The app is fully functioning after downloaded to the client.
- Client resources and capabilities are fully leveraged.
- Work is offloaded from the server to the client.
- An ASP.NET Core web server isn’t required to host the app. Serverless deployment scenarios are possible (for example, serving the app from a CDN).
However, there are some downsides to Blazor WebAssembly hosting:
- The app is restricted to the capabilities of the browser.
- Capable client hardware and software (for example, WebAssembly support) are required.
- Download size is larger, and apps take longer to load.
- .NET runtime and tooling support are less mature. For example, limitations exist in .NET Standard support and debugging.
Even with all downsides listed above, this solution looks really promising, so let say no more and start with our app!
But before building it we still need to install .NET 5.0.100 SDK
We are not going to write everything from scratch but instead, we start from Blazor WASM template by running the next command:
dotnet new blazorwasm -o ToDoList
It will generate a
ToDoList folder with the following project structure:
Let’s quickly go over some pieces of it, but if you bootstrapped Blazor projects before it will look very familiar to you.
Pages folder – Contains
razor files for pages (routable), while Shared holds all shared components used in Pages. wwwroot is a public folder that contains all static data such as
css style files and
index.html . As you can see template shipped with Bootstrap components library. Still, one minor version behind (on the date of writing this article Bootstrap has version 4.4 while template shipped with version 4.3.1, Microsoft should update templates faster, really…)
Also, you may notice
open-iconinc which is an open-source sibling of Iconic, the icons library. Kinda cool addition, but you always could switch to any other icon library, e.g. Bootstrap introduced their own icon library
Program.cs –is the main entry point of the whole app. for now it contains only a few basic lines. Services can be configured with the
ConfigureServices method on the host builder.
builder.Services.AddSingleton<IMyDependency, MyDependency>(); .
Configuration can be supplied via the host builder
builder.Configuration. This is the main difference between Blazor Server and WebAssembly is the type of builder. As you could see for WASM application it is “WebAssemblyHostBuilder”
RootComponents specifies the HTML tag ID where
App will be rendered, in this case <app>…</app> tag:
Another interesting line here is <script> tag that points to
blazor.webassembly.js file. This JS file is responsible for:
- Downloading the .NET runtime, the app, and the app’s dependencies.
- Initializing the runtime to run the app.
App.razor — The root component of the app that sets up client-side routing using the Router component. The
Router component intercepts browser navigation and renders the page that matches the requested address.
This structure of components and their name styles reminds some other popular frameworks, such as VueJS.
_Imports.razor — Includes common Razor directives to include in the app’s components (.razor), such as
@using directives for namespaces.
NOTE: You could run the app by pressing F5 in VS Code. Alternatively to run it from CLI use dotnet run command. The app will be served at http://localhost:5000.
But finally, let get to our app… For real this time!
Let's add a new page file named Todo.razor to the app in the Pages folder. Notice
@page "/todo" binds this page to the new route.
And update the navigation bar inside Shared/NavMenu.razor:
Let's run the app with
dotnet watch run command and we should see our link to TODO list on the NavBar that will bring us to our new page.
dotnet watch run will track the changes in the project files and refresh the browser. Similar to the live server. So you don't need to run it every time
Ok. The next step is to introduce TodoItem model. Let's create a Model folder where we create TodoItem.cs file with next class:
Now when we have a model let use it inside Todo component. Go back to Pages/Todo.razor and add
@code section at the bottom of the file. This section holds all component business logic. You could think about this section as a nested Class within the Component.
We will add a private field of
Let's use this list to render TodoItems into UI. We are going to use unordered list tag <ul> for that. Just use C#
foreach to loop over the items.
As you may notice the
@symbol is used for UI logic code insertions. Similar to mustache syntax used in many popular Frontend frameworks.
Alright looks like we missing an input to actually add data to
todos list. Go add it:
Now our ToDo list looks way more attractive!
But it is not functional =( Let’s go fix it!. First we need to hook up
@onclick handler to the button. For that create a private method within
@code section and add
@onlcick attribute so it will look like this:
Ok, but we missing a data binding between
<input> tag for Todo Title and component class. To do so introduce another
string field and bind it to the
<input> tag with
And some spicy logic to the
Let’s test what we have so far. Because we using
dotnet watch run the app already reloaded in the browser and we could try adding some “todos”.
It works! …. But looks boring… We will drop some extra functionality to gasp Blazor skills. Add UI tags and bind them to the model so we could edit “todos” title and status, but also count of undone tasks:
Let see what we have this time?
Delightful! Try changing values and see how reactivity works with model binding in Balzor. As you may notice when you change the title in the input, it reflects only when you unfocus the input element. This is the default behavior of Blazor
@bind attribute. It triggers on
<input> “onchange” event:
But of course, this behavior could be changed.
And that is it. We build our SPA ToDo list using Web Assembly with Blazor framework. To stop serving the app press
Ctrl + C.
I really like how Blazor framework evolves and becomes more and more attractive, but like all other frameworks, it has its pros and cons. I like how easy it is to implement routing and model binding. Another benefit I like is a strong typed C# with and .NET 5.0 support that brings a lot of new features to the table.
As for WebAssembly: yes it works fast but only when it's loaded. As you could see from Lighthouse report below, we build a pretty small app but it already has a size of around 9mb. So if your Web App is supposed to be more fast and lightweight from a user perspective, I will suggest you to avoid WASM.
But if you build an online tool (e.g. photo editor or smth heavyweight and static) that loads once and performance very very good and fast — then WASM is a solution for you!
Source code could be found here: https://github.com/godszerg86/BalozorWASM_ToDO
In the next article, we will try to host a .NET 5.0 WASM application using Docker image locally on your machine.