React vs. Blazor: a comparison

Luke Canvin

If you want to write a dynamic web application in 2019 then the chances are that you will reach out for a JavaScript solution, and that solution will probably be React. But what if you don’t want to learn JavaScript or one of its higher-level siblings? What if you have a .NET back end and don’t want to write the same code twice?

Blazor

Enter Blazor, a framework which lets you create a web application in C# and run it entirely in a browser. Blazor is a bleeding edge solution (it’s currently in preview) but it can already do the job of React and more. As a developer who knows React well, here are the things I did and didn’t enjoy while working on my first Blazor project during our Dev Camp this year. Familiarity with React is helpful but is not necessary, and please note that this article is about Blazor client-side not its server-side relative.

Components

Blazor’s approach to UI is exactly the same as React’s, asking you build your UI up by nesting components and markup. Like a React component, a Blazor component can be passed parameters from its parent component and have internal state.

Blazor components are written as classes, and they have lifecycle methods reminiscent of React component classes. As React transitions to hooks this feels slightly outdated, and I found myself missing hook features like property-wise binding and memoisation.

Blazor borrows Razor syntax from ASP.NET to combine C# and markup. React has the upper hand here with JSX: in Razor, C# is a “guest” inside the markup, and in practice this is less powerful than having the markup as a guest inside the code. Visual Studio also currently has a frustrating amount of trouble formatting Razor files in a consistent way.

Routing

React focuses on drawing to the DOM, and as such if you want routing within your application you must implement it yourself or use a third-party library like react-router.

In Blazor, routing is feature of the framework. It’s driven by a simple, declarative directive in your component and it supports dynamic paths. I appreciated the simple design of this feature and prefer it to having to pick a third-party library in React.

Data binding

Blazor provides a declarative syntax for data binding. I hit issues when trying to “chain” bindings (binding to a parameter which is already bound inside a parent) and find explicitly passing React-style “setter” function parameters more powerful and easier to debug than a special syntax.

Interop

As Blazor uses the .NET runtime it shouldn’t be surprising that you can import and run almost any C# code (almost since you can only use things that can live inside a browser, so no .NET HttpClient for example). This is extremely useful when you have code which needs to run both client and server-side. It’s something you can do with a React too, but only as long as your back end is Node.

Despite Blazor having lots of features, there will still be situations where you need JavaScript. This could be a particular must-have JavaScript library, or a browser API which Blazor doesn’t proxy (for example FileReader).

Blazor makes it easy to invoke JavaScript from inside an application with its JSRuntime service. This allows you to call any global JavaScript function (i.e. one which has been declared on window) and parse its return value. A nice touch is that the service also understands asynchronous JavaScript functions and treats them like their async C# counterparts. The API is still brittle and disjointed, but could be greatly improved in the future with automatic type inference for code with Typescript declarations.

Dependency injection

Blazor has built in dependency injection. This means we can declare inside a component that it requires a particular service, and at runtime Blazor will provide an instance of that service to the component. This is a really useful feature for separating logic from components and it’s something that I’ve not been able to do satisfactorily in JavaScript (modules provide something close but it don’t have the same flexibility as managed dependency injection).

Prerendering

Prerendering happens when the server executes a client side application, takes a snapshot of the DOM and uses this for the markup it sends to the client. This makes the application appear to load faster in the browser and allows search engines to scrape your landing page more easily. Both Blazor and React support prerendering, but you will want to use a matching server stack (React with Node and Blazor with ASP.NET).

Debugging

React is about as easy to debug as any other JavaScript code (though you will probably want a source map to account for JSX transpilation). It also has excellent browser tools to find issues and assess performance.

Blazor is definitely less polished in this regard. At the moment debugging is limited and troublesome or impossible to set up. It’s should be improved as the framework matures, but for my project I was forced to use to print debugging.

Building

React only requires JSX transpilation before it can be run in a browser, and this fits well into a JavaScript bundler like webpack.

Blazor has a completely different build process and it won’t integrate with a node-based JavaScript bundler. There’s nothing to stop you using a bundler for all your non-Blazor assets, but I did miss file watching in particular while developing in Blazor.

Summary

Although React and Blazor have much in common, a large part of their value comes from which server stack you combine them with. Blazor feels near-complete and I would strongly consider using it in a situation where I had a critical .NET library or .NET server-side code which needed to run in the browser. Otherwise I would stick with React on the basis of its maturity, tooling, JavaScript integration and hooks. Blazor will no doubt improve in these areas in the future and I will be keeping my eye on how the framework evolves.

If you would like to explore Blazor further then we recommend the Awesome Blazor repo for examples of it in action.