Blazor, the new kid on the block in the web frontend world since 2018. It understandably gets compared to the likes of Angular, React, and Vue all the time, but it is a whole other beast. For starters, Blazor is not JavaScript based; it is .NET based.
Blazor, in its pure web form, has two flavors. There is Blazor Server and Blazor WebAssembly. No matter what version you prefer, the development experience is the same; you use C# and HTML to build your frontend application. But, haven’t we tried this before? Wasn’t there something called Silverlight that also let us build frontend web applications with C#? There sure was, but Silverlight was based on a plugin system that was Windows only; it could never survive in this day and age where so much of what we do on the Internet happens on mobile devices or on non-Windows devices. Blazor manages to bring us .NET to the web frontend using only open web standards. No plugins are required; Blazor applications run in the same secure sandbox as JavaScript-based frameworks but with added flexibility, depending on your choice of Blazor flavor.
Blazor WebAssembly
Blazor WebAssembly is the version of Blazor that comes the closest to JavaScript frameworks like Angular and React in that the code is executed in the user’s browser instead of on a server. This is however not done by transpiling C# code into JavaScript in a TypeScript kind of way; instead, Blazor makes use of WebAssembly, an open web standard that defines a binary code format for running applications in the browser sandbox. In other words, WebAssembly is a platform that can run applications. WebAssembly, or Wasm, became a W3C recommendation in December 2019. One of the main objectives of Wasm was getting better, even near-native, performance out of web applications. While JavaScript is definitely a powerful language, it still lacks the performance, features, and maturity from the more enterprise-ready managed languages like C# and Java.
After Wasm was officially a supported standard in the most common browsers, someone at Microsoft decided to see if they could get .NET to run on that new platform. After a while, a proof of concept was ready and demoed using a stripped down version of Mono. This proof of concept turned into a development team; the development team turned the demo into a product.
Blazor Wasm is evolving fast, but it is also limited by WebAssembly itself in some ways. The current version of Wasm doesn’t allow direct manipulation of the DOM and has no multithreading. Both limitations are being addressed in future versions of WebAssembly.
Creating a Blazor Wasm Project
The template can be configured in multiple ways. First there are some built-in authentication types like single user accounts or the Microsoft Identity Platform. Choosing one of these options will adjust the template with boilerplate code that enables either local users or users to log in using their Microsoft account. More information on the built-in security can be found at https://docs.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-6.0. Configure for HTTPS is checked by default and sets the configuration so that the application only responds to HTTPS requests.
Blazor Progressive Web Apps
Default manifest file for a Blazor Wasm app
Besides a manifest.json file, there’s also a service worker added to the project. Service workers are JavaScript code that acts like a proxy between the application and the network. Service workers enable offline access to web applications by caching data and fetching updated data once the network is available.
More information on building a Blazor-based PWA, including the manifest file and service workers, can be found at https://docs.microsoft.com/en-us/aspnet/core/blazor/progressive-web-app?view=aspnetcore-6.0&tabs=visual-studio.
Exploring the Blazor Client Project
Blazor makes use of the Razor framework that made its debut in ASP.NET MVC. If you have done ASP.NET MVC before, you will recognize a lot of things, but there are some Blazor-specific things in there as well.
Counter page in the default Blazor template
Razor is a framework that mixes HTML and .NET code. It allows us to declare variables and bind them to HTML elements or trigger .NET methods from HTML elements.
In Razor, an @ sign signals the start of a .NET statement. An example of this is @page "/counter" at the top of the page. Page is an attribute; instead of using square brackets, we use the @ sign again to set this attribute to the counter page. In case of the page attribute, it is used for Blazor’s navigation service. This page can now be accessed through https://the-webapps-url/counter.
The @code directive specifies the code block for this specific page; those code blocks are scoped to the page they are declared in. The sample code declares a private field called currentCount in a normal C# way. That field is bound to in HTML by prefixing it with an @ sign, <p role="status">Current count: @currentCount</p>.
This page also demonstrates updating a data field by calling a method. The HTML button specifies an @onclick event. This type of event is different from the HTML/JavaScript combination you might be used to because of the @ sign. Once again, this signals a .NET statement, in this case calling a method that is declared within this page. The IncrementCount method increases the integer field with 1, immediately updating the UI as a result.
As you can see, only one element is marked, so only one small part of the page is changing. This results in fast web applications that feel more native than, for example, ASP.NET MVC applications that often rely on page reloads and server callbacks.
Blazor in .NET 6
Program class of a Blazor WASM application
Blazor in .NET 6 makes use of top-level statements to trim down the size and complexity of the Program file. As you can see in Listing 5-3, there is no namespace, no class declaration, and no Main method declaration; the Main method is still there but it is hidden away as a syntactic trick; should you inspect the intermediate language, you will find the Main method there. More information on top-level statements can be found here https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top-level-statements.
This sample application sets the App component as root component for this web application. The system uses selectors like selectors in CSS to select the correct element in the index.html file where to inject the components. In the default template, the #app selector is used. If you open the default index.html file in the wwwroot folder of your project, you will find a div with app as ID. This is the div where our Blazor application will be hosted.
Default app component
The App component is the most top-level component in a default Blazor application. It configures the router, to enable page navigation, and considers the router to be similar to a NavigationPage if you’re more used to Xamarin Forms or MAUI development. The router specifies found and not found views; the not found view will be shown whenever navigation triggers an HTTP 404.
Another rootcomponent being added is the HeadOutlet. Its selector specifies that the component is added to the head section of the HTML file rather than replacing it. The component itself allows us to easily set the title of the page to be reflected in the browser’s tab or title bar.
After adding the root components, we register an HTTP client as a scoped service into .NET’s dependency injection framework. We are registering our services here so that we can inject them in our Blazor pages later on as we will see in a minute.
Blazor Component System
MainLayout.razor
Adding parameters to a Blazor component
Setting a parameter
Index.Razor’s page declaration
Since the url to this page is the root url of the application, this page will be initially loaded and placed on the @Body part of MainLayout. The PageTitle we specify here is what will go in the HeadOutlet root component we declared in Program.cs and will become the page title.
If we now go back to the counter page we have previously seen, you can clearly see where the navigation menu comes from and what part of the MainLayout is replaced with the code from the counter component.
Creating Blazor Pages
We have mentioned both Blazor pages and Blazor components before. Blazor pages are components with an @page directive, giving them a url for the routing system to navigate to.
Attributes in FetchData.razor
We’ve run into the page attribute before. As a reminder, it configures the route for this page; when navigating to https://<hostname:port>/fetchdata, we will be redirected to this page. The inject attribute is Blazor’s version of dependency injection. It will search for the HttpClient type in the ServiceCollection that we’ve seen in Listing 5-3 and set an instance of that type to the Http member.
In Listing 5-6, we see the Razor code of the FetchData class. As mentioned before, Razor can be explained by HTML mixed C# snippets. Take the if statement for example. It is prefixed with an @ sign, signaling that a code statement will follow. The statement is followed by brackets, embedding the code between the brackets in the statement. This specific statement will prevent a potential NullReferenceException on forecasts. The code loops over the forecasts collection, but if that collection is not loaded yet, it will crash. The interesting part here is that that if statement will actually get reevaluated as soon as the forecasts collection is updated. This is because Blazor has an internal method called StateHasChanged. When this method is triggered, Blazor will re-render its current state, take a diff between the old and the new render tree, and apply that diff to the old one that is still on screen. This results in only a partial refresh of the page instead of a full page reload. StateHasChanged can be called manually by us, but Blazor calls it internally whenever a property on a component changes or when an event on a component is triggered. This results in an easy way to show a “loading” message while data is being fetched from an API.
Razor code
Code block in a Razor file
OnInitialized
OnInitializedAsync
OnParametersSet
OnParametersSetAsync
OnAfterRender
OnAfterRenderAsync
ShouldRender
Running a Blazor App
So, now we know how Blazor works. But how do we go from entering a URL in our browser to a running Blazor application? Is it a plugin like Silverlight was? Do we need browsers that support Blazor? The answer is simple. Blazor is not plugin based; it runs on WebAssembly. All we need is a browser that supports Wasm, and all modern browsers do. To understand how Blazor is loaded into a browser, we need to step back and look at a basic web server. A web server in its purest form is a server that hosts a bunch of files. Those files get downloaded to the browser of someone who enters the URL that routes to your webserver. Webservers have had a form of conventions, for example, if no specific html file is specified in the URL, the server will, by default, look for index.html or default.html. Other web servers, like IIS, for example, also take this convention into account; index.aspx is still the startpage of an ASP.NET application. A Blazor WASM application is served as a static website; there’s no need for an IIS server to make the calculations or run the .NET code. All we need is a website that can serve static content, in this case HTML, CSS, JS, and DLL files.
There are some interesting files in this folder. I have removed a lot of the files from Figure 5-8 for brevity, but the interesting ones are still there. Before we go into the actual files, notice that there are three versions of the files? That is because the output generates every file in a normal way and twice using a different type of compression. The .gz files are gzipped, while the .br files are compressed with Brotli. It is up to the server that serves our application to detect what the optimal type is for the client requesting our files.
Let’s start with the actual .NET 6 runtime. As you can see, the assemblies are there, in part. All the System.* files are part of the .NET 6 assemblies, while all of the Microsoft.* files are specific for Blazor. When publishing a Blazor application, the framework will run a linker process. This process will remove unreachable or unused code from the generated intermediate language files, and it will remove unused binaries from the output (tree shaking). These operations are not perfect. It is very important to perform a complete end-to-end test of your application after publishing. There is a possibility that the linker was too aggressive and that your application suddenly behaves in unexpected ways. To fix this, the linker can be configured to ignore certain modules by adding nodes to the project file. The complete documentation on how to do this can be found at https://docs.microsoft.com/en-us/dotnet/core/deploying/trim-self-contained.
The blazor.boot.json file contains information about the project. It contains a list of assemblies, with hashes to prevent tampering, that need to be loaded in order for the application to start.
blazor.webassembly.js is the glue between the web world, where the HTML lives, and the native world, where .NET lives. As mentioned before, we currently cannot directly access the DOM from within the .NET Blazor code. To work around this, Microsoft created this JavaScript file. Since we cannot interact with the elements in the visual tree from within Blazor, but we can call JavaScript functions through JS Interop, this file can serve as a bridge from Blazor to the visual tree.
BlazorWasmDemo.dll contains the application code we have written.
dotnet.wasm is the native Webassembly code that contains the instructions to load up the .NET runtime environment in the Webassembly sandbox.
Launching the app again takes significantly less time and resources. That is because the browser caches as much as possible. There’s even quite a big chance that your users won’t notice the three seconds it takes to load your application. That is because there is a big difference between speed and the perception of speed. As mentioned before, the first thing loaded into the browser is the index.html file. That file contains logic to show “Loading…,” while the WASM and .NET runtimes are being downloaded and started; replace that with a nice loading animation, and your application will be perceived as loading quite fast.
Blazor Server
Blazor Server is a second flavor of Blazor. It looks and feels very similar to Blazor WASM, but the big difference is in the underlying architecture. Instead of running inside web assembly, it actually runs on a server, hence the name. Blazor server uses a SignalR connection to send requests to a server that handles all the instructions and sends back changes to the DOM. Before we go any deeper, let’s see what SignalR is.
SignalR
SignalR has been around for quite some years. It is a framework that allows developers to easily implement real-time communication between clients and servers. It enables server code to directly call methods on clients instead of clients having to poll for data on the server. A simple example of this is a chat application where client A pushes a new message to the server; the server then calls client B with the new message as parameter. All of this is possible due to Websockets, which is the underlying mechanism of SignalR. But it goes one step further. On platforms that don’t support Websockets, SignalR can automatically fall back to older transport protocols. Because of this fallback functionality and a load of abstractions on top of the Websockets, API SignalR has quickly gained a lot of popularity.
Thanks to SignalR we have server–client and client–server communication. By leveraging this, Microsoft built a server-based web framework that does not do page reloads; instead, they receive a piece of DOM through the SignalR connection. We can see this in action by launching the counter page of the default Blazor Server template. The project template is mostly the same as Blazor WASM, from App.razor to MainLayout down to the code of the components. Just like in Blazor WASM, when you inspect the HTML code and click the counter button, you will see that only the element containing the number is updated; the rest of the page is never reloaded.
Program.cs from a Blazor Server application
Program.cs in .NET 6 contains top-level statements once again, making for a cleaner file without a class declaration. If you’ve used ASP.NET MVC before, the instructions in this file might look very familiar. That is because the SignalR connection is powered by ASP.NET, so we need to bootstrap that framework as well. Similar to Blazor WASM, we start by registering services in the built-in dependency injection (DI). After registering services, we have basic boilerplate code to enable HTTPS, set up routing, and start the app.
There are two Blazor-specific calls in this file. The first one is builder.Services.AddServerSideBlazor();. This call registers Blazor-specific services into the DI container, services like the NavigationManager for navigation between Razor components or the IJSRuntime to enable JavaScript interop. The exact code of this method can be found on GitHub https://github.com/dotnet/aspnetcore/blob/main/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs. The second Blazor-specific call is app.MapBlazorHub();. This call opens the connection to the Blazor SignalR hub. A SignalR hub is a class that contains communication logic between clients and a SignalR server. In this case, the hub will contain the logic that enables receiving instructions and data from a client, generating a DOM diff and sending it back to the client. The SignalR part of Blazor Server is also open sourced on GitHub at https://github.com/dotnet/aspnetcore/tree/main/src/Components/Server/src/BlazorPack.
Rendering mode in Blazor Server
Static: All components are rendered into static HTML, meaning that there is no connection to a Blazor SignalR server and pages have no Blazor functionality.
Server: The webserver builds the HTML, connects to SignalR, and activates all Blazor functionality. After the server is finished with all that, the browser will receive the HTML and render everything. This is the slowest option but with the most consistent results.
ServerPrerendered: This option uses a technique called hydration. Hydration is a known pattern in most popular SPA frameworks; it takes the best of both static and server render modes to find a middle ground between performance and functionality. Hydration is a two-step process; the first step renders static HTML which gives users the illusion of a fast page load. At this point, the page has appeared on screen but there is no Blazor functionality. In the second step, there is a piece of JavaScript in blazor.server.js that will open the connection to the SignalR hub and hydrate the already rendered page with functionality; the page basically re-renders invisible to the user. The blazor.server.js file is a file included in the Microsoft.AspNetCore.Components.Server.dll assembly. It gets injected in your application’s output automatically.
ServerPrerendered is the default option, and looking from an end-user perspective, it is the most interesting one performance vs. functionality-wise. However, do be careful with automated tests. We have run into issues where the test runner is clicking a button before the re-rendering has taken place. The re-rendering usually happens fast, from a human perspective. Automated tests are executed by machines and can happen faster than the re-rendering.
As soon as _Host.cshtml is finished loading, we are in Blazor land. From here on out, everything works exactly the same as Blazor WASM, development-wise. Feel free to compare the code of the Razor components that are in the default templates for server and WASM projects; they are the same components. Since we’ve already discussed Razor components in the Blazor WASM section of this chapter, we won’t go over it again.
Blazor Desktop
Back in Chapter 4, I briefly mentioned that Blazor was also available for building desktop applications. Using Blazor in WinForms or WPF applications is possible, thanks to a control called the BlazorWebView. The BlazorWebView is a component that hosts a WebView2 control, which in its turn is a component that can render HTML based on Microsoft’s Edge browser. The BlazorWebView also knows how to initialize and run Blazor.
Unfortunately at the time of writing, there are no project templates available for WPF or WinForms that include the Blazor setup, so for now we will have to do it manually. For this demo, we will start from a WinForms project. The procedure for WPF is very similar so it shouldn’t be a problem to follow along.
The modified project file
Note that at the time of writing both of these packages were still in Prerelease. That is because the Blazor Desktop efforts are part of MAUI, Microsoft’s cross-platform mobile framework which we will talk about in Chapter 6. MAUI was supposed to be released together with .NET 6 but they missed that mark. Instead it will launch somewhere in 2022.
Adding the BlazorWebView to a form
We are adding the BlazorWebView from the constructor. First we create a new instance of ServiceCollection, which is needed for dependency injection inside the Blazor part. We call the AddBlazorWebView extension method on the ServiceCollection to wire up all of the Blazor framework-related services. Next we create an instance of the BlazorWebView WinForms component; we let it dock to all sides so that the BlazorWebView will take up all available space on the form. The HostPage is the index.html file we have copied over from another Blazor project into the wwwroot folder. The Services property is the ServiceCollection where all Blazor services are registered.
Just like before, we add a rootcomponent to the div with ID app. In this case, we add the only component our application currently has, the counter component. We are free to add as many components as we want; we can copy an entire Blazor application in here with the MainLayout, the routing mechanism, and so on.
Since this is basically a control running on a form, we are free to mix Blazor components with WinForms controls to create fully hybrid applications.
Wrapping Up
As we have seen, Blazor is Microsoft’s answer to popular client-side frameworks like Angular and React. It allows us to build powerful, dynamic, and beautiful web applications using .NET instead of JavaScript. Blazor comes in different flavors, fully client-side, thanks to the power of WebAssembly of client-server, thanks to real-time communication over SignalR.
Besides being a framework for building web applications, Microsoft has been investigating bringing it to different platforms. The result of that can be found in the Prerelease version of the BlazorWebView. With the BlazorWebView, we can share Blazor components from the web to WPF and WinForms and eventually even to mobile applications with MAUI.