The following post is a write up of my attempt to optimize a Blazor client side application.
Blazor
First of all what is Blazor?
Blazor is a cool new piece of technology for creating browser based web applications using C# instead of JavaScript. The idea is that Blazor let’s us run client side C# code directly in the browser through WebAssembly.
Running C#, but also other languages, in the browser is very appealing to me since it allows for much more flexibility when developing for client side web. Currently we are limited to JavaScript since this is the only language supported by all browsers. Aside from the pains of waiting for universal browser support before adopting new language features in JavaScript there are a number of short comings in JavaScript.
Why Blazor?
My biggest complaint about JavaScript is lack of built-in support for types. As a workaround a lot of developers write code in TypeScript rather than JavaScript since it adds support for build time typings. Generally this works very well, but it adds another level of complexity to the tooling. Blazor avoids this problem completely since C# is a strongly typed language with types built in.
Another benefit of Blazor is that we can write frontend code and backend code using the same language. An obvious benefit of this is automatic synchronization of types between backend and frontend. Sure, you can achieve this in TypeScript applications as well through protobufs and code generation, but again this complicates the tooling.
C# in the browser also makes the transition to client side development much easier for traditional C# developers since they don’t have to learn a whole new technology stack (JavaScript, TypeScript, Webpack, etc).
Blazor also benefits from C#’s similarity to Typescript, which should make the transition to C# much easier for people who approach this from the Typescript side.
This article focuses on client side Blazor, but I should point out that there is a second flavor of Blazor that runs on the server. To learn more about the difference checkout my client side Blazor vs. server side Blazor article.
Challenges with Blazor
Blazor is very interesting, but there are a number of challenges that will have to be overcome before it can be a viable alternative to traditional JavaScript applications.
By far the biggest problem is application size from bringing in the .net runtime, which at the time of writing is close to 3.3MB. This means even a simple application will have a 3.3MB footprint, which is unheard of in web developer circles.
Granted the dependencies are immediately put into browser cache, which means subsequent request are much faster since you no longer have to download the .net dlls + wasm runtime. However this still makes for an incredibly slow experience the first time someone visits your site. Yes, it’s even slow on a desktop with a broadband connection.
I’ve heard some people say 3MB isn’t really a big deal and point to large payloads of other popular websites. In my opinion this is a flawed comparison even if you can point to websites with even bigger runtime payloads. The main reason for this is that these sites may still offer a relatively short time to first meaningful paint if the dependencies are downloaded asynchronously and just happen to add up to a lot of bytes in the end. Blazor on the other end can’t start to render anything until the entire 3MB of payload has been received by the browser. This makes for a horrible first meaningful paint rating.
Microsoft is currently working on trimming the fat of Blazor applications, but in the short term it will still be bigger than what I consider “acceptable”. Supposedly the first official version will be around 2MB, which is a great improvement percentagewise, but needs to be reduced further.
Demo
In order to track performance improvements in Blazor I decided to use it to create a simple bio section for my blog. Check it out to get a feel for Blazor performance.
Luckily Blazor doesn’t have a runtime dependency on a .net host since the compiled output contains all necessary .net runtime dependencies out of the box. This is great since my blog is a NodeJS/Express application and it would be a deal breaker to have to introduce an additional server to host my bio page.
After compiling my Blazor code all I had to do was map up the generated _framework folder as a static folder in Express like so:
Now, all I have to do to load my Blazor app is add a script tag to my html page.
Basically this is all you need to bootstrap a Blazor application in Node/Express.
During page load the browser will download all required dependencies like the .net dlls and the wasm runtime.
The first load will be very slow since the browser has to download 3.3 MB worth of dependencies. Subsequent loads are faster since the dependencies have been put in browser cache. To see the cached dependencies just load up your browser tools and inspect your browser storage.
CDN
Currently there is not much I can do about the total size of the payload, but I can at least put the dependencies behind a CDN. This ensures that visitors can download dependencies from a location geographically close to their own location. Also, I don’t have to pay bandwidth charges on these large downloads. Take a look at the network tab to see the dependencies be downloaded from CDN cache instead of directly from my server.
One note about CDNs. Blazor downloads the runtime using an integrity attribute with a hash checksum, so you have to make sure your CDN doesn’t alter the page content in anyway. I use Cloudflare and had to disable automatic minifcation since it changes the page and breaks the checksum.
Conclusion
I think Blazor is super cool as a concept, but worry that bundle sizes will present a big problem for mainstream adaptation, at least in the short to medium term. Even if the payload is reduced to 2MB it’s still a dealbreaker on mobile and unreliable networks. I don’t think it’s a huge problem for internal business applications, but public mobile websites should think twice before using it.
I still think wasm I very promising for web development though. If Blazor can’t bring the bundle sizes down to an acceptable level, I suspect a more nimble runtime will emerge and take over this space.