C#You know C#, but not well enough
Let’s look at various nuances and gotchas in C# syntax.

Hi everyone. My name is Vladyslav Furdak, and I’ve been working in .NET development for 13 years. I’m a Microsoft MVP, and I’m involved not only in development but also in mentoring engineers and helping companies with hiring. If any of that sounds relevant to you, feel free to reach out.
The last major project I worked on was a SaaS portal where the entire client-side application was built with Blazor WebAssembly (WASM). In this article, I’ll share my hands-on experience with the framework and my perspective on its strengths and weaknesses, its market niche, and when it makes sense to use it. By the way, I'm also the author of a NuGet package called BlazorNexus that makes routing in Blazor WASM applications more convenient.
Blazor is essentially C# running in the browser. How is that even possible?
Let’s start with a quick refresher on WebAssembly. WASM is a technology that first became available in browsers in 2017. At its core, it’s a virtual machine that executes WebAssembly bytecode in a sandboxed environment - conceptually similar to containerization. High performance is achieved because instructions written in other programming languages can be compiled into efficient code that runs on the client machine.
To run code in WASM, an application written in C++, Go, or Rust must be compiled into a *.wasm module. WebAssembly can also call JavaScript functions to execute them in the browser's context. From a .NET perspective, this is implemented via calls to special methods in dotnet.js, which then invoke browser APIs.
At this point, the Mono runtime comes into play. Mono has been compiled for WASM, so it can run inside the WebAssembly sandbox. Once you have .NET in the browser and a programmatic way to interact with browser APIs, it’s logical to build a front-end framework in C# on top of that. That framework is Blazor.
Conceptual diagram:
There have been many attempts to enable the development of interactive browser applications without JavaScript and HTML.
| Platform | Release year | Native browser support | Requires | Open development | Contributors |
|---|---|---|---|---|---|
| Java Applets | 1995 | No | JVM plugin | No | Sun |
| ActiveX | 1996 | Yes (IE) | ActiveX | No | Microsoft |
| XUL | 1999 | Yes | Mozilla | No | Mozilla |
| Java Web Start | 2001 | No | JVM plugin | No | Sun |
| Flash/Flex | 2004 | No | Flash Player plugin | No | Macromedia/Adobe |
| Silverlight | 2007 | No | Silverlight plugin | No | Microsoft |
| XBAP | 2008 | No | .NET Framework 3, Windows | No | Microsoft |
| JavaFX | 2008 | No | JVM plugin | No | Sun |
| Yew, Percy | 2018 | Yes (Mozilla, Chrome, Safari, Edge) | WebAssembly | Yes | Open source community |
| Uno | 2018 | Yes (Mozilla, Chrome, Safari, Edge) | WebAssembly | Yes | Inventive / open source community |
| Blazor | 2019 | Yes (Mozilla, Chrome, Safari, Edge) | WebAssembly | Yes | Microsoft |
| Flutter on Web | 2021 | Yes (Mozilla, Chrome, Safari, Edge) | WebAssembly | Yes |
By the way, here’s the Google Trends chart for the query "Blazor". While frameworks like Angular have higher absolute popularity, Blazor shows steady interest, with periods of growth. Angular, on the other hand, shows a downward trend.

I’ll say it upfront - Blazor is not a competitor to React, Angular, or Vue. Comparing it to those frameworks is the wrong framing. Blazor solves a much narrower problem - giving C# developers a more modern and flexible way to build web applications compared to MVC or WebForms.
| Blazor falls behind | Blazor is on par | Blazor wins |
|---|---|---|
| The number of NPM packages is an order of magnitude higher than NuGet. | Rapid SPA development with modern UI suites like Telerik, Radzen, DevExpress, Syncfusion. | Web on mobile, or MAUI hybrid apps - this competes with Cordova/Ionic. |
| The job market for experienced front-end specialists is orders of magnitude larger in the JS ecosystem. | You can support both SPA and server-side rendering formats. | Building PWAs where bundle size is not critical. |
| Blazor cannot directly call browser APIs - it goes through JSInterop. This adds performance overhead and still requires writing JavaScript. | Sharing a single C# codebase with desktop applications, or using API clients generated by NSwag on the front end. | |
| Hot Reload is less stable than in the JS ecosystem. | Bringing in C# engineers to build complex web apps, and lowering the entry barrier for C# developers moving into modern front-end work - Razor in Blazor is very close to cshtml. | |
| Better refactoring and debugging tooling thanks to C#. |
So, to summarize, Blazor is an ideal fit for:
Blazor is a poor fit for:
This does not mean you cannot ship these kinds of projects on Blazor - it just means it will typically take more effort than with other frameworks.
What are real-world cases where Blazor requires JavaScript integration?
These are browser APIs. There are NuGet packages like Blazored.SessionStorage that hide the JS layer and isolate the interop. If you’re curious how it works, you can read the package source code.
Imagine a chat window like Skype. A lot of new messages arrive, and you need to scroll the message area to the beginning of the new messages.
This breaks down into three tasks:
You can see this implementation in the ChatViewModel.Initialize() method, which runs when the chats page opens.
The JS invoked from the Blazor app can be found in the browserHelpers.js source file.
For example, Google reCAPTCHA or a payment gateway widget. If you're lucky, there will be a ready-made package like GoogleCaptchaComponent. If not, you’ll have to build a wrapper yourself. I cannot claim that all widgets are covered by Blazor packages.
This is a real requirement, but it becomes complicated if part of the logic lives in Blazor and part in JS.
A non-obvious challenge is platform-specific limitations. For example, in Safari, not only does type casting behave differently than in Chrome, but there are also restrictions around programmatic audio playback.
If you use a UI kit like MudBlazor and wire a button handler that triggers audio playback via JS interop, nothing will play - and you might not even see an error.
That happens because under Apple policy, audio playback must be initiated by a direct user interaction. In other words, the handler must be attached directly to the user action. In our case, we trigger playback programmatically via JS interop (C# WebAssembly -> JS), which Safari can block.
What should you think about when starting a new Blazor project?
You typically have several hosting model options (I won't retell the whole document, but these are the key models):
Component render modes are not the same as the hosting model above. It mainly matters for Interactive Server and Auto, because a pure WASM app uses Interactive WebAssembly as the default component mode.
In short:
| Mode name | Description | Interactivity |
|---|---|---|
| Static Server | Components are rendered on the server and sent to the client as static HTML. No interactivity. Good for content that does not need user interaction after the page loads. | No |
| Interactive Server | Components are rendered on the server with interactivity provided via SignalR. User actions are sent to the server, business logic runs there, and UI updates are pushed back in real time. | Yes |
| Interactive WebAssembly | Components run in the browser via WebAssembly. All required code is downloaded to the browser, allowing the app to work independently after the initial load. | Yes |
| Interactive Auto | Starts as Interactive Server, then continues as Interactive Client. This is comparable to SSR in modern full-stack frameworks like Next.js. | Yes |
In general, Blazor lets you combine different rendering models. You can render some parts on the server, then hydrate on the client. Or you can stream a component progressively, which makes Blazor feel closer to Next.js mechanics.
Blazor naturally fits MVVM because components support data binding out of the box.
There are several ways to organize component code:
If your project is bigger than a personal landing page, I’d recommend separating ViewModels. Here's an example of ViewModel abstractions showing how you can structure them for your project.
You can also nest ViewModels inside other ViewModels, connecting them via events. I consider this the simplest way to decouple logical component relationships from the view-to-view wiring.
If your state is extremely complex and looks more like a state machine, MVVM can become messy due to non-deterministic state. In that case, consider FLUX (Redux) via Fluxor. This unidirectional data flow approach was very popular in React.
A more detailed description of these and other settings can be found here.
In my opinion, the default routing system based on string constants in component directives is not very convenient because endpoints can change. You also need to think through back navigation within an SPA. For example, you might have several pages that can lead to a payment page, and then you need to return back correctly.
I handled these scenarios while modernizing routing for WASM Blazor apps in the Blazor.Nexus package. For any moderately complex app, you’ll still have to design a routing strategy anyway.
The package is in alpha, but it’s already usable and covered with unit tests. I keep it in alpha because there are still edge cases to address.
The default approach in Blazor, like in most .NET stack apps, is to use resx files. This approach has been proven over years of ASP.NET MVC and WebForms development.
A more advanced approach is Portable Object (PO) files. Unfortunately, the most well-known PO SDK, Orchard Core Localization, does not work in a WASM environment. It works only with Blazor Server or a standard ASP.NET backend. Examples can be found here. A possible workaround is to serve translations via an API from the server.
For WASM Blazor apps, you can consider the lightweight Toolbelt.Blazor.I18nText package.
More about i18n and l10n in Blazor can be found in my Blazor i18n and l10n guide.
In the project I worked on, we used an existing proprietary library that generated code from our internal translation format. The client company built it for themselves and reused it across their applications.
Be aware that some Blazor libraries are no longer maintained. Some authors do not maintain them actively, and they can go years without changes. I recommend being very selective about what you bring into a project, and if it’s something small - it can be better to implement it yourself.
A fairly large list of libraries can be found in the Awesome Blazor repository.
Not sure what to start with? I suggest the following stack:
If you need a PWA, there is a Blazor PWA guide from Microsoft. Also, pay attention to a package that supports automatic PWA updates.
For component testing, you can use bUnit - a testing library for Blazor components.
Blazor is not a silver bullet. I recommend weighing the pros and cons before starting a new project on this framework. The technology is fairly niche, but it has been production-ready for quite some time.

Senior Software Engineer & Microsoft MVP with 15+ years building scalable backend systems. Founder of Ukraine's largest .NET community. Writes about clean architecture, design patterns, EF Core, Azure, and engineering career growth.
FollowGet practical .NET tips and architecture insights delivered to your inbox when new articles are published.