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
To say simply it is an interface between compiled binary code (mostly C++ and Rust) and the environment where it executed — in our case web browser (JavaScript). All modern browsers support Wasm so you don’t need to worry about this part.
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 bootstrap-icons
.
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.
E.g:
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.
NOTE.
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 List<TodoItem>
there:
Let's use this list to render TodoItems into UI. We are going to use an unordered list tag
- 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. Let's introduce another string
field and bind it to the <input>
tag with @bind
attribute:
And some spicy logic to the AddTodo
method.
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 or SPA ToDo list using Web Assembly with Blazor framework. To stop serving the app press Ctrl + C.
Conclusion.
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.
It really helps C# developers who like to develop some frontend but doesn’t really want to dive into JavaScript nightmare =).
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.
Top comments (2)
Nice project! I found it when I searched for posts on web assembly. I'm exploring new patterns in software development. My feedback: I'm excited to build portable websites in my favorite programming language. The wasm compiler takes care of everything for me in a future portable website.
That's is true! As I said above - we could avoid JS nightmares. Looks pretty good to me. Looking forward Microsoft brings more releases (wish for a smaller package size).