Editor’s note: This article was last updated 12 May 2022 to include information on the inner workings of Blazor.
There are several frameworks developers can choose from when building single-page applications, with three of the most popular being Angular, React, and Vue. However, building SPAs with these three frameworks requires JavaScript.
But, what happens when a developer is interested in building SPAs but doesn’t want to deal with the caveats of JavaScript? In this article, we’ll explore React and Blazor, two different client-side options for minimizing JavaScript, comparing the two in terms of features. Let’s get started!
Blazor is a Microsoft UI framework that follows a unique approach, leveraging C# .NET and WebAssembly to create SPAs that run in web browsers. Essentially, Blazor allows a developer to build interactive client-side applications with HTML, CSS, and C#.
On the other hand, React is a declarative, efficient, and flexible JavaScript library for building user interfaces and UI components. React and Blazor share the similarity of being client-side frameworks and libraries for building rich, interactive, and modern client-side applications.
One unique feature of Blazor is its JavaScript interoperability, meaning a Blazor app can invoke JavaScript functions from .NET methods and .NET methods from JavaScript functions.
Blazor is composed of two major projects, Blazor WebAssembly, the client-side, and Blazor Server, the server-side. The Blazor Server implementation uses the more traditional .NET app approach and requires a server to connect to a frontend via SignalR for message passing. On the other hand, the Blazor WebAssembly implementation packages the project into a static bundle that can be deployed. In this article, we’ll focus on comparing Blazor WebAssembly with React.
Blazor Wasm runs with a compiled version of Xamarin’s Mono framework. The end result is that you can actually run a .NET C# application directly in the browser. Blazor compiles your .NET code into Wasm, which can then be deployed as a static bundle. For older browsers, Blazor uses asm.js to compile down to a common framework that older browsers can support.
Blazor also supports interoperability with JavaScript libraries. You can still use an npm module with a Blazor project by importing it with the build. To directly interact with JavaScript packages alongside your C# code, Blazor provides the IJSRuntime and JSRuntime in C#. Check out the Microsoft docs for a more detailed explanation.
Let’s create both a Blazor app and a React app and compare what comes out of the box. To create a new Blazor project, you need to download and install the .NET SDK and run the following command in your terminal:
dotnet new blazorwasm -n BlazorApp
The blazorwasm
command means that we’re creating a new Blazor project with WebAssembly. The -n
flag is for the name of project:
The program.cs
file contains the main methods needed to get the WebAssembly app up and running:
We’re also mounting our app and selecting a tag with an ID of app
. The HTTP client is loaded using a dependency injection technique, and the HTTP modules in our Blazor app are built on top of the JavaScript Fetch API:
In a React app, index.js
is similar to program.cs
in a Blazor app. In the code snippet below, the React DOM renders our app component, and the element with the ID of root
is selected as our root component:
Instead of JavaScript, Blazor uses C# as its programming language, leveraging the existing .NET ecosystem of .NET libraries. With this feature, C# developers are able to expand their skillset from only writing backend code with C# to building full-stack web and mobile applications with C# as the programming language.
At first, C# may look and feel unnatural to JavaScript developers, but once you get a good grasp of C#, you can build powerful full-stack applications written entirely in C#.
React uses JavaScript as its programming language. Essentially, web developers still get to write the language they’re the most used to. And, with React Native, you can build web, Android, and iOS applications that share the same code snippets.
When creating an SPA with React, the Create React App toolchain is recommended. The Create React App toolchain will, by default, initialize a React app configured with JSX, which is a syntax extension for JavaScript. It acts like a templating engine that allows you to write HTML in React and also write JavaScript in HTML:
Blazor uses the Razor template engine, which has been around for years. The templating engine is not new in the C# and the ASP.NET ecosystem. It’s used to embed server-side code into webpages using C# and ASP.Net.
Just like JSX, the Razor template engine allows you to write C# code in your markup.
Blazor projects are slow on the client-side because you have to download the entire dot net runtime along with the necessary DLL
libraries on your browser. Additionally, Blazor apps have latency issues. So if you’re building a web application that’s going to be accessed by people across the globe, Blazor shouldn’t be your go-to framework.
Another important thing to note is that during development in a Blazor app, you don’t get to enjoy the hot-reload features that you have in React. As a result, your development process is likely going to be slow because you have to hit the restart button to restart your app:
The lighthouse score from the diagram above clearly shows that Blazor apps have some serious performance issues. The initial page load time is slow because the necessary dependencies have to be downloaded on initial page load:
In this area, React shines. As a UI library, the core React package is as lean as possible. It is fully optimized out of the box to build blazing-fast, modern client-side applications using a component-based paradigm.
At the time of writing this, Microsoft has announced five new editions of Blazor, including Blazor WebAssembly and Blazor Server.
On the other hand, the React ecosystem is extremely large to the point where you can find packages on npm for almost everything you’re trying to implement. React is fully backed by Facebook, and it has a lot of community support given that it changed a lot about how client-side applications are built. Also, React feels natural to a web developer because it is still JavaScript.
React’s selling point is “learn once, write anywhere.” Essentially, with React, ReactDOM, and React Native, you can build out highly interactive and rich frontend web, Android, and iOS native applications. This makes it easy for most companies to build out solid products and platforms at a relatively low cost because they’ll just hire a developer who’s good with React and React Native.
At the time of writing this article, React has over 188k stars on GitHub. It’s inarguably one of the most loved JavaScript libraries in general. Meanwhile, Blazor has about 28k stars on GitHub.
Given the fact that Blazor was initially released in 2018 and is relatively new to the developer community at the time of writing, popularity may not be a valid reason to mark it down.
The Blazor PWA provides support for developers to build high-end progressive web apps. In a React application, adding PWA support is a breeze. Running the following command will initialize a React app with a service worker file added:
npx create-react-app my-app --template cra-template-pwa
Blazor Native, which is experimental at the time of writing, essentially allows developers to build native mobile apps with Blazor using the Mobile Blazor Bindings. Using C# and .Net, developing Android and iOS apps with Blazor is actually a possibility.
Blazor Hybrid apps are a combination of a native and a web UI in a single app. Using Blazor, you can write the native UI for your app and also create a web UI in your app, thereby creating the possibility of using Blazor to build both your web and mobile applications.
Essentially, you’ll share code snippets across your web and mobile application. This is definitely a good time to be a C# .Net developer.
React uses React Native to build native mobile applications, enabling React developers to build mobile apps with React. With React Native, you can use native UI controls and have full access to the native platform.
React Native is used in production today, but Blazor Native is still in the experimental phase of its development. When compared to React Native, Blazor Native has little community support.
React, like other JavaScript frameworks and libraries, uses npm and Yarn as package managers for managing dependencies. In a Blazor WebAssembly app, you can easily install a package in one of the following ways:
To install a package with PackageReference
, navigate to the Blazorize.csproj
file and add the package inside of the ItemGroup
tag:
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
To install a package with .NET CLI, navigate to the root of your app directory in your terminal and run the following command :
dotnet add package Microsoft.AspNetCore.Blazor.HttpClient --version 3.2.0-preview3.20168.3
Check out these guides to learn about the .NET CLI and Paket CLI.
By default, React offers two main approaches for handling state in components. A component can either handle its own state or data, or it can accept data via props. In dealing with the HTTP section, we’ll see how a component handles its own state.
Below is an example of how a component accepts data via props from its parent component in a React app:
// Parent Component export default function Blog() { const blogPosts = [ { id: 1, title: 'This is the title', content: 'This is some random content', }, { id: 2, title: 'This is the title', content: 'This is some random content', }, { id: 3, title: 'This is the title', content: 'This is some random content', }, { id: 4, title: 'This is the title', content: 'This is some random content', }, ] return ( <> <BlogCard blogPosts={blogPosts}/> </> ) }
We passed in blogPosts
, which is an array of objects containing blog posts, to the BlogCard
component, which is the child component:
// Child Component export default function BlogCard( { blogPosts } ) { return ( <> {blogPosts.map(blogPost => ( <div className="blog-post" key={blogPost.id}> <h1>{blogPost.title}</h1> <p>{blogPost.content}</p> </div> ))} </> ) }
Now, when we render the child component BlogCard
, we can pass in the list of blog posts via props. To learn more about props in React, check out the official docs.
In Blazor, a child component can receive data from its parent components via parameters:
// Child Component <h2>@Title</h2> <p>@Content</p> @code { // Demonstrates how a parent component can supply parameters [Parameter] public string Title { get; set; } [Parameter] public string Content { get; set; } }
When you render a BlogCard
component, you can pass in a Title
and Content
and it will be rendered accordingly:
<BlogCard Title="What is Blazor?" Content="Blazor is a web UI framework using C#/Razor and HTML..." />
In a React application, the router does not come preconfigured or installed. The React Router package is mostly used to implement client-side navigation.
According to the documentation, React Router works anywhere React is rendered, including in React Native projects. The API is really simple, yet it handles a lot of powerful features like URL parameters, component redirects, lazy loading, page transitions, nested routing, and more.
To set up routing in a React app, typically you would install the react-router-dom
package and wrap your entire app in the index.js
file with the browser router module from the React Router DOM package. Then, in your App.js
component, you would render your pages with the route module from the React Router DOM.
The images below illustrate how routing works in React:
In a Blazor WebAssembly client-side application, the routing system leans on ASP.NET’s existing routing engine. You can map a route to a Blazor component by using the @page
directive and then the path that you want to link to at the top of your file.
To navigate to another page, you have to use the NavLink
component, which is similar to how the NavLink
component works in the react-router-dom
:
Programmatically navigate between pages in a Blazor app by injecting the NavigationManager
at the top of the file with the code below:
@inject NavigationManager NavManager
Then, call the NavigateTo
method in a function on the NavManager
you’ve injected:
@inject NavigationManager NavManager <p>Learn more about us</p> <button @onclick="navigateHome">Go back home</button> @code { private void navigateHome() { NavManager.NavigateTo(""); } }
To explore handling HTTP requests in a Blazor application, we’ll create a FetchPost.razor
file in the pages directory:
@page "/http" @inject HttpClient Http <h1>Blog post</h1> @if (posts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Title</th> <th>Body</th> </tr> </thead> <tbody> @foreach (var post in posts) { <tr> <td>@post.title</td> <td>@post.body</td> </tr> } </tbody> </table> } @code { private BlogPost[] posts; protected override async Task OnInitializedAsync() { posts = await Http.GetJsonAsync<BlogPost[]>("https://jsonplaceholder.typicode.com/posts"); Console.WriteLine(posts); } public class BlogPost { public int id { get; set; } public String title { get; set; } public String body { get; set; } } }
There are three interesting things that are going on here. First, we’re injecting HttpClient
, a service that helps us make HTTP calls using the HTTP client. As of Blazor v3.1.0, you need to add Blazor HTTP Client to your Blazorize.csproj
file.
Then, we invoke HttpClient
using the GetJsonAsync()
method to fetch a JSON from the defined endpoint. Finally, we create a result type with the type BlogPost
that is able to pick out the relevant data found on the property post
based on the incoming JSON structure.
To inject HttpClient
, add @inject
HttpClient Http
at the top of the page. We invoke HttpClient
to actively fetch the data. We do so by defining OnInitializedAsync()
, a lifecycle method that is guaranteed to run when the page is initialized.
One of the benefits of using a library like React is the flexibility of its tools. React doesn’t provide an HTTP client like Blazor, so a React developer can decide to make HTTP requests using the Fetch API, Axios, or even XHR.
For HTTP requests, we’ll use the JavaScript Fetch API. The code below demonstrates how to make a simple HTTP request in a React app using the built-in JavaScript Fetch API:
// post.js import React, { useState, useEffect } from 'react' export default function Post() { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/posts") .then(response => response.json()) .then(post=> { setPosts(post) setLoading(false) }) }, []) return ( <div> { loading ? ( <p>Loading...</p> ) : ( posts.map((post, i) => ( <div> <p>{post.title}</p> <p>{post.body}</p> </div> )) ) } </div> ) }
Notice that we are calling the endpoint on the useEffect
Hook, telling React that our component needs to fetch the data from our API after rendering. Then, when our request is successful, we need to call setPosts
and pass our post
objects from the API. For more details on how useState
Hooks work in React, check out the official docs.
Choosing the best frontend framework to build an SPA depends on a lot of factors, including team preference, ecosystem, performance, and scalability. In this article, we compared React and Blazor to see how both of these awesome frameworks work.
React and Blazor are similar in a few ways, and they can be used to accomplish the same tasks. So, which one should you choose to work with? You might have to consider their popularity, the type of project you’re building, scalability, and maintainability.
Hopefully, with the information in this article, you should be able to make informed decisions when choosing a frontend framework for your next project. I hope you enjoyed this article. Happy coding!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowSimplify component interaction and dynamic theming in Vue 3 with defineExpose and for better control and flexibility.
Explore how to integrate TypeScript into a Node.js and Express application, leveraging ts-node, nodemon, and TypeScript path aliases.
es-toolkit is a lightweight, efficient JavaScript utility library, ideal as a modern Lodash alternative for smaller bundles.
The use cases for the ResizeObserver API may not be immediately obvious, so let’s take a look at a few practical examples.
19 Replies to "React vs. Blazor: Minimize JavaScript in your SPAs"
Huge mistake there in performance, blazor in this case compiles to web assembly, so is not true that the client needs to download the entire. Net ecosystem plus the dll. Misleading and untrue.
I have an incredible amount of bias, but I absolutely love how elegant Blazor is. The implementation of components with getters and setters? Fantastic telegraphing. Binding is just dead simple. Building a modern front end framework off of such a strong base like ASP.NEat just makes a ton of sense.
A tad biased towards React my friend and get your facts right before trying to play down the benefits and performance of Blazor… It is the future…
I’m a React and Blazor developer but I prefer Blazor because I can integrate FE and BE using only one enviroment and language.
Mono runtime and app dll’s are downloaded “once” in Blazor wasm. From then on, the only interaction with the server is for fetching json data for binding to UI. Blazor is literally blazing fast after the initial app download. With the upcoming AOT changes in .Net 6, Blazor will be much more faster and efficient even in initial download size and performance.
Blazor is definitely the future. It’s crazy how quickly it has been able to get to the point where it’s comparable to the major JavaScript frameworks that have been at this for so long. I was very apprehensive at the start of going the Blazor route, but after using it, I never want to look back.
You’re comparing apples to oranges here. React is a library, and Blazor is a framework. For example, React can be used standalone by just loading a script from a CDN. This article does a disservice to both technologies.
Absolutely !!! I was about to write the same comment.
I see a lot of enthusiastic comments below each of Blazor article but not agree. I worked with Blazor not so much but after one MVP I can say that I don’t like Blazor. Maybe some days it will be ready to touch my hart, but not today:
1. Blazor is slow. Maybe it is only for me, but I can see a little lag after each click;
2. has almost no 3rd party libraries. You need to create your own UI-components or choose between just a couple of UI-libraries (half of them are paid). And these UI-libraries often ugly and have bugs even on demo pages;
3. “You can use one language for front and back” (of only we had Node.js). Yes, but no. You still need to know JS, because WebAssembly has no access to browser API’s by design. You can not work with canvas directly, as an example. Also scrolls, querySelectors, elements positions and so on;
4. using JS-libraries is complicated. You need to download source codes and put them into `wwwroot` folder, then add js- and css-files into your `index.html` (you can use webpack for this, but you have to do it yourself, Blazor doesn’t have this functionality out of the box);
5. no LESS/SASS, you also need webpack (not sure how to do it for css-isolation).
Maybe some of these issues have been fixed in fresh releases.
The “Stars” is for the old Blazor repo, Blazor is part of ASP.NET Core and is therefore maintained with the rest of ASP.NET Core https://github.com/dotnet/aspnetcore. I think this changed over a year ago with the first release.
@Ivan, LESS/SASS is supported using a preprocessor, its the same as normal css isolation in blazor, you’d do something like Page.razor.less which would be compiled into Page.razor.css before the build and would then get packed as you’d expect a plain isolated css file. Blazor can use any JS based UI elements, so yes although there isn’t that many Blazor native packs, it’s not hard to use JS based ones where you logic is still within the C# side. Blazor is still a bit slow, but most of the slowness is due to incorrect writing of the page, it’s still in its infancy perf wise and has a long way to go to catch up to javascript based frameworks. I find it responsive enough so long as you don’t use asynchronous JS interop and make sure you use the virtualizer feature for long lists.
Interesting that no one’s given Server-side Blazor consideration (which is what I’m using). The first-page initial download is rediculously small (because it’s all on the server!), and once that SignalR hub has your front-end wired up to the back-end server via a WebSocket connection – the interaction is pretty fast. I’ve even done terrible things like have a timer on the server firing UI changes down to the browser 30 times a second (like JQuery animation used to) – and it responds well! (no, this is not production code.)
One major advantage Server-side Blazor has is access to data. Your code is probably already running in Azure / AWS, and you’re a lot closer to the data than your client-side cousins, so if you take the hit of not trying to hide behind an IHTTP interface of some kind, you can crack open the Entity Framework, and grab that data! No translation to JSON objects necessary. So in this way, you may actually asynchronously perform updates to the UI (a little) faster than you could with front-end code relaying information via a Service’s API. We also get the benefit of having web-events poking your back-end to inform it of database changes, which can then be pushed down to the front end. In server-side Blazor, that kind of thinking seems a little easier to implement as a result of the underlying SignalR connection. Also, your code feels more like a part of the server, and that logical separation of concerns, which shifts the way you think about the code.
I don’t think at this point there’s any getting around the fact that some JQuery knowledge is still required. But seriously, that’s bare-bones thinking. Despite this fact, at least 99% ouir development hasn’t needed any javascript interaction, except via 3rd party UI components. Who on the javascript side doesn’t use a 3rd party library like Bootstrap for UI components, and css magic? The same is true for Blazor. You’re likely to accomplish most of your work with 3rd party libraries (there are both commercial and open-source options) so complex ui components and areas we just can’t touch (such as Canvas, as previously mentioned) are now well within our grasp with C# interactions.
Isn’t there always a library out there tryingf to solve all your problems? The only problem now, is finding it (NuGet) and using it!!
1) Blazor Wasm is only slow for the initial app download after that it blazingly fast. However MS continues to improve this, it is after all a new framework.
2) For most of the points you raise there I would say just checkout awesome Blazor https://github.com/AdrienTorris/awesome-blazor
There are plenty of UI components for Blazor, you do not need to create your own UI-Components, dismissing them as ugly is a matter of opinion.
3. Yes you can use C# for both front end and back end. The fact that you can use JavaScript for both front end and back does not take away from the fact that you can use C# in a similar way. That simply means you have a choice. You can work with the canvas using a Blazor canvas extension and Blazor gives you reference to HTML elements via @ref and ElementReference, so you can deal with element positions and so on.
4. There is nothing complicated about using JS libraries, many can be installed automatically via NuGet, and Blazor/C# wrappers already exists for many popular ones such as for chartJs, ironically Canvas etc. There may be need to setup up some js interop but that is usually a matter of copy and paste. How hard is it to add JS and CSS refences to an index.html file? Is there any website that does not use them?
Yes Blazor is new but it is remarkable how well it already stacks up to JS frameworks. One should not approach it with the same mindset of using JS. I know JS but for the Blazor production apps I have already completed I have hardly ever needed to write any lines of JS beyond to setup some interOp with a third party JS library. However a lot of JS libraries I have need have an equivalent C# implementation or a wrapper already from the days of asp.net MVC and webforms. This point of knowing JS is exaggerated, Blazor was designed for C# developers to write SPA application with very little need if at all to know JS.
The ‘direct’ comparison of communication between components omits Cascading Values and Parameters.(see: https://docs.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-5.0).
Blazor apps have latency issues. So if you’re building a web application that’s going to be accessed by people across the globe, Blazor is not your go-to framework.
If it’s WASM then once the initial package is downloaded to the client there are no other calls needed other than network calls like fetching data from a database. The app essentially functions like an old school Windows Forms app.
If it is an application (say an app for graphics designing) where the user is going to spend considerable time, then waiting for couple of seconds more is worth having it.
I dont have ASP experience but This sounds biased
Testing Blazor components (with bUnit) is way simpler than testing React components.
Fantastic blog. 1000 times better than YouTube video.
Learning is tiresome through videos. Good old text will eventually win over !
Blazor Server, Blazor Hybrid are simply awesome. SignalR is fast, direct data access, finally! With Blazor Hybrid I wrote an Android app, deployed in record time. WASM not so much (yet), etc. Wait until .NET8 with multi-threading. Microsoft is finally going in the right direction. Sorry but this article is biased. I understand the allure of React (which is not even a platform) but do some serious analysis first before posting half truths!