Blazor WebAssembly in Practice: Maturity, Success Factors, Showstoppers

ASP.NET Core Blazor is Microsoft's framework for implementing web-based applications, aimed at developers with knowledge of .NET and C#. It exists alongside other frameworks such as ASP.NET Core MVC. About two and a half years after the release of Blazor WebAssembly and based on our experiences from many customer projects at Thinktecture, we want to have a close look at the following questions: What is the current state of the framework? How can you successfully use Blazor? And where does it have limitations?

In diesem Artikel:

Blazor WebAssembly in Practice: Maturity, Success Factors, Showstoppers
Christian Liebel ist Consultant bei Thinktecture. Sein Fokus liegt auf Webstandards und Progressive Web Apps. Er vertritt Thinktecture beim W3C.

Thanks to my colleagues Patrick and Sebastian for their valuable insights into their projects, whose learnings I could consider in this article.

One Blazor in three flavors

The framework currently offers three different execution and hosting models:

First, there is Blazor Server, which relies on a stateful server to run the application. In this case, the rendering of Razor templates to HTML takes place on the server. Via a SignalR connection, user interactions are reported from the web browser to the web server, triggering a re-render there. The server returns a diff of the changed HTML content over the same connection (see Figure 1). Any .NET code can be executed on the server, and all platform-specific interfaces can be used. In addition, the code on the server is protected from third-party inspection.

Figure 1: How a Blazor Server application works

In contrast, Blazor WebAssembly allows the application to run completely on the client as a Single-Page Application (SPA). When the application is invoked, all required source files are loaded from the web server. This can be a static web server, with no special requirements. At runtime, HTTPS and WebSockets connections (e.g. via SignalR) are used to exchange data with APIs (see Figure 2).

Figure 2: In the SPA model, all source files are transferred to the client

The term WebAssembly refers to the byte code of the same name, which can be executed by the JavaScript runtime environments of all common browsers. Code from any programming language can be compiled into this byte code and thus made executable in web browsers. The prerequisite for this is that all interfaces used are also available in the browser. For example, random access to the file system or device interfaces is generally not possible from the browser. With Blazor WebAssembly, .NET assemblies are executed on a version of the Common Language Runtime (CLR) compiled to WebAssembly. If Ahead-of-Time compilation (AoT) is used, most of the C# code is compiled directly to WebAssembly, which can bring performance advantages, for example for performance-critical applications such as image processing.

Finally, there’s a third variety: Blazor Hybrid. Here, the web-based user interface is embedded via WebView in a .NET Multi-platform App UI (MAUI), WPF or Windows Forms application. Rendering is done by the application itself, WebAssembly is not used at all. The source files are delivered as a part of the application package, so the application can be run offline. In addition, as with Blazor Server, all platform-specific interfaces can be accessed.

Blazor WebAssembly is the tool of choice in many cases

In this article, we will mainly focus on Blazor WebAssembly, which seems to be more suitable for many types of web applications: This is because Blazor Server requires the operation of a server as well as a permanent and stable connection with low latency to it. Delays are already noticeable to the user in the range of a few dozen milliseconds. If the connection is lost altogether, the application can no longer be operated at all, as Figure 3 shows. Since the server is stateful, it can only serve a certain number of users simultaneously. This can be a problem for scaling.

Figure 3: If the server connection is interrupted, Blazor Server apps can, by default, no longer be used

Offline scenarios also cannot be reasonably implemented with Blazor Server, as would be desirable for Progressive Web Apps (PWA). With Blazor Hybrid, on the other hand, only desktop or mobile applications can be built, which requires deployments via the usual platform-specific paths such as installers or app stores—these applications cannot be run in the browser. Conversely, however, apps written in Blazor WebAssembly that run in the browser could also be packaged for desktop and mobile platforms, such as via Electron or MAUI. An overview of the above points is provided by Table 1.

Blazor WebAssembly
Blazor Server
Blazor Hybrid
Calling arbitrary platform-specific interfaces
(only if packaged)
(only on server)
Support for offline capability
Executable in browsers
Packageable for mobile/desktop plattorms
Code can be kept secret

Table 1: Overview of the functional features of the individual hosting models

In the projects we have been involved with at Thinktecture, Blazor WebAssembly has predominantly proved to be the more suitable choice. Only in one project where a machine control system was involved Blazor Server was chosen since it is not possible to communicate directly with the unit from the web browser, and the control PC is located directly next to the machine, which is why offline scenarios are not relevant. The approach is also suitable if the binary code is not to be transferred to the user’s computer for confidentiality reasons.

Does it have to be Blazor?

Blazor WebAssembly was first released in May 2020, joining veteran frontend frameworks like Angular. Compared to these, Blazor WebAssembly is still a young framework with corresponding growth potential. With the availability of AoT compilation, server-side prerendering, native dependencies, as well as improvements in interoperability with JavaScript as part of the release of .NET 6, a meaningful level of maturity was reached for the first time in November 2021, so that we at Thinktecture can generally recommend the use of the framework—given suitable conditions in the respective projects. Microsoft is also constantly improving Blazor: With .NET 7 in November 2022, more useful features were added, for example, Blazor components can now be used directly as web components; there have been improvements in routing and data binding. However, some interesting features, such as multithreading, unfortunately missed their way into the release.

The choice of a suitable technology is often made at the very beginning of a project. This depends, among other things, on the experience and development potential of the team of developers. Angular uses many concepts known from the Extensible Application Markup Language (XAML) and of the Windows Presentation Foundation (WPF). The TypeScript programming language used in Angular was designed by Anders Hejlsberg, the same language designer responsible for C#. For these reasons, Angular is also a reasonable choice for a team of .NET developers.

Blazor is not a magical Windows-to-web converter

Where there is only .NET and C# expertise, Blazor may be more appropriate. But even with Blazor, developers can’t get around Hypertext Markup Language (HTML), Cascading Style Sheets (CSS), and, in the end, JavaScript. Non-trivial Blazor projects will still need to interact with JavaScript, especially for integrating existing JavaScript libraries. While there are only a few Blazor components, there are plenty of JavaScript-based graphics libraries, PDF generators, and the like. While they can be used directly from frameworks like Angular, Blazor WebAssembly requires the cumbersome use of the JS interop bridge and often the writing of appropriate binding code in JavaScript. However, JS interoperability has improved significantly with .NET 5: it is now possible to load JavaScript modules directly instead of having to make objects available on the global window object. Also, accesses to JavaScript object references now work sufficiently fast.

Similarly, SPAs written with Blazor WebAssembly must integrate with the same authentication flows as other web applications. Currently, authentication in Blazor is based on the outdated oidc-client.js library, a planned rebuild for .NET 7 unfortunately did not make it into the release.

Blazor also uses the Component-Based Software Engineering (CBSE) approach: An application is broken down into many small, reusable components. The framework has also recently been further developed in this respect, for example, since .NET 5 it has been possible to isolate CSS styles per component, a feature that has been available since the first release of Angular.

Furthermore, the framework runs in the browser, so developers need to be able to understand error messages and debug there as well. Blazor WebAssembly is therefore not a miraculous machine that simply converts .NET apps to the web. It is the same abstraction layer that Angular, React, or Vue represent—including all advantages and disadvantages.

Developers should also use Windows systems. Even though Blazor apps can be implemented with JetBrains Rider or on Visual Studio for Mac, and the support has undergone several improvements, development including debugging only works really well in Visual Studio 2022 on Windows. For example, Hot Reload, the immediate reloading of the application after changes in the source code, only works in this development environment. Rider did not have full support for the new SDK immediately after the release of .NET 7.

Mobile users and firewalls can cause problems

Another decisive factor for the choice of technology is the target group of the application: Is it a publicly accessible application or an internally used tool? The reason for this is the bundle size of Blazor WebAssembly applications. Even a simple Hello World application with Blazor WebAssembly is already 5.7 megabytes large (see Figure 4), while a comparable Angular application starts at about 150 kilobytes. The basis for comparison here are the Hello World applications that are created via dotnet new blazorwasm-empty or ng new. The transferred bytes of the uncompressed productive builds that can be built with ng build or dotnet publish -c release are counted. The used versions were the .NET SDK 7.0.100 and Angular 15.0.0. With activated Brotli compression, the size of Angular can be reduced to about 50 kilobytes, if you additionally switch off culture information in Blazor WebAssembly, you can reach a bundle size of 2 megabytes. Blazor also caches the DLL files, so they don’t have to be downloaded again the next time they are requested. While there have been significant improvements recently, Blazor’s size overhead will never go away entirely: Since .NET isn’t web-based, appropriate translation layers must be included. If the application is also to be used on mobile, the sheer scale of Blazor apps can already be the dealbreaker. The initial runtime performance of Blazor apps is also not ideal: after Blazor boots up, the .NET assemblies are downloaded, thus resulting in a long loading time for the user, during which all they see is a loading screen.

Figure 4: A "Hello World" Blazor application already requires 5.7 megabytes to be transferred.

In one project, the firewall deployed at an end customer prevented the .NET assembly DLL files from being downloaded, rendering the Blazor app completely useless. It is understandable that security solutions consider downloading a large number of DLL files as a threat. Unfortunately, AoT compilation does not remedy this, but actually makes the size situation even worse, as the resulting WebAssembly bundles sometimes become twice as large. In addition, the number of transferred DLLs is only reduced, since for Reflection, for example, .NET assemblies still have to be transferred. Whether this can be further optimized is questionable. In this respect, Blazor WebAssembly is rather unsuitable for B2C solutions where the environment such as firewalls or mobile use cannot be controlled. But even for B2B applications or entirely internal solutions, the size and runtime performance of applications can become problematic: One customer’s employee dialing into the corporate network from home via VPN had to wait about 20 seconds for a Blazor application to launch. Another customer switched from Blazor WebAssembly back to Angular in frustration due to the significantly worse runtime performance, since he had already used Angular before and Blazor offered no advantages.

Built-in solutions for better performance

The server-side prerendering of Blazor WebAssembly, which has been available since .NET 5, can provide a partial remedy here. The page is pre-rendered on the server side, and the finished HTML is transferred to the client (see Figure 5). This in turn requires the operation of a server configured for this purpose. Without prerendering, the source files of the application can be transferred to any static web server and executed from there. Furthermore, developers must additionally test this behavior.

Figure 5: With server-side prerendering, the HTML is already assembled on the server, which reduces the perceived loading time.

Once loaded, however, a Blazor WebAssembly app can achieve roughly the same speed as any other web application—with the same pitfalls that apply to all other frameworks: Such as rendering too much data. An essential factor for runtime performance is the number of displayed nodes in the Document Object Model (DOM). A list with thousands of rows should not be rendered directly. Alternatives are paging or virtualized lists. For the latter, Blazor WebAssembly even offers a built-in solution, unlike Angular and co: The component optimizes the rendering of list data by only actually inserting the currently visible records into the DOM. The underlying data can either be kept in memory or dynamically reloaded as needed. This results in optimal runtime performance even for very long lists. Another option added with .NET 7 is the experimental QuickGrid, which also includes paging.

Conclusion

Blazor WebAssembly is primarily suited for internal enterprise applications where the environment can be clearly controlled, where there is existing .NET and C# expertise, where a move to other approaches is not feasible, and where Windows is used as the development environment.

But this is not to be understood exclusively: One of our customers does use Blazor WebAssembly for a B2C web application. The users tolerate the somewhat longer loading time, which is still shorter than with one or the other Windows application, the customer can develop the app himself as far as possible thanks to C# knowledge and then gets external support for the JavaScript integration and bug fixes in the project.

Due to its unusual architecture and poor scalability, we recommend avoiding Blazor Server if possible and prefer Blazor WebAssembly instead. Only this flavor of the framework fits with other single-page app approaches like Angular, React, or Vue, supports offline capability, and can also be hosted in Electron or MAUI using the exact same source files.

In short, the Blazor WebAssembly concept for forms-over-data business applications works very well, it is sufficiently stable and mature. In addition, Microsoft is constantly developing the framework family further.

Kostenloser
Newsletter

Aktuelle Artikel, Screencasts, Webinare und Interviews unserer Experten für Sie

Verpassen Sie keine Inhalte zu Angular, .NET Core, Blazor, Azure und Kubernetes und melden Sie sich zu unserem kostenlosen monatlichen Dev-Newsletter an.

Diese Artikel könnten Sie interessieren
.NET
Blazor WebAssembly: Debugging gRPC-Web with Custom Chrome Developer Tools

Blazor WebAssembly: Debugging gRPC-Web with Custom Chrome Developer Tools

If you are working with Blazor, gRPC is a big issue for transferring data from APIs to clients. One issue of developing with gRPC-Web is debugging the transmitted data because the data is in an efficient binary message format. In this article, I will show you how to solve this problem with the help of my NuGet.
17.11.2022
.NET
Entity Framework Core: User-defined Fields and Tables

Entity Framework Core: User-defined Fields and Tables

The requirement to store additional fields, unknown at development time, in a relational database is not new. Nonetheless, none of the projects I know of are willing to change the database structure at runtime. What if there is a project which needs dynamically created fields and doesn't want or cannot use entity–attribute–value model or switch to No-SQL databases?
20.09.2022
.NET
Incremental Roslyn Source Generators in .NET 6: Adapt Code Generation Based on Project Dependencies – Part 5

Incremental Roslyn Source Generators in .NET 6: Adapt Code Generation Based on Project Dependencies – Part 5

The Roslyn Source Generator, implemented in the previous articles of the series, emits some C# code without looking at the dependencies of the current .NET (Core) project. In this article our DemoSourceGenerator should implement a JsonConverter, but only if the corresponding library (e.g. Newtonsoft.Json) is referenced by the project.
08.07.2022
Unterschiede
.NET
Blazor WebAssembly vs. Blazor Server – Welche Unterschiede gibt es und wann wähle ich was?

Blazor WebAssembly vs. Blazor Server – Welche Unterschiede gibt es und wann wähle ich was?

Das Blazor Framework von Microsoft gibt es inzwischen in drei "Geschmacksrichtungen". Die erste ist Blazor WebAssembly, die zweite Blazor Server, und zu guter Letzt gibt es noch Blazor Hybrid. In diesem Artikel wollen wir uns die zwei "echten", also Browser-basierten, Web-Anwendungs-Szenarien WebAssembly und Server anschauen.
04.07.2022
Three different textured walls
.NET
Dependency Injection Scopes in Blazor

Dependency Injection Scopes in Blazor

The dependency injection system is a big part of how modern ASP.NET Core works internally: It provides a flexible solution for developers to structure their projects, decouple their dependencies, and control the lifetimes of the components within an application. In Blazor - a new part of ASP.NET Core - however, the DI system feels a bit odd, and things seem to work a bit differently than expected. This article will explain why this is not only a feeling but indeed the case in the first place and how to handle the differences in order to not run into problems later on.
31.05.2022
.NET
Asynchrone Operationen: Blazor WebAssembly für Angular-Entwickler – Teil 5 [Screencast]

Asynchrone Operationen: Blazor WebAssembly für Angular-Entwickler – Teil 5 [Screencast]

Eine Webanwendung will natürlich auch mit Daten gefüttert werden. Doch diese müssen irgendwo her kommen. Nichts liegt näher als diese von einer Web API zu laden. Dieser Screencast zeigt, wie asynchrone Operationen in Blazor funktionieren und welche gravierenden Unterschiede es zu Angular gibt.
26.05.2022