The following post is a write up of my attempt to optimize a Blazor client side application.
First of all what is Blazor?
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.
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
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.
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.
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.
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.