The popularity of React is undeniable. When bright-eyed developers think about creating a new app, React is usually the first thing that crosses their minds. React has all become the defacto front-end library of choice.
React is comfortable. React is familiar. React is used by almost everyone. React is the library you learn if you want to make yourself an employable front-end developer.
The truth is, front-end developers have painted themselves into a corner with React. To understand why React became so popular, we have to go back to 2013, when React first made its grand debut. AngularJS was the dominant and most popular option for web applications at the time, and boy did AngularJS suck.
However terrible AngularJS was, everyone was using it. AngularJS had some interesting ideas, but confusing terminology (provider, service, factory) and some performance issues meant building an app was sometimes half the battle.
Let me state the obvious bias here: I am on the Aurelia core team, everything I say is tainted with bias. However, I want this to be an accurate summation of what both Aurelia and React are. If I make a misleading or completely incorrect claim, let me know, and I will fix it.
React became the most popular kid in school because the alternatives were much worse. I actually worked with React and came from an AngularJS background. Like other developers in 2013, I was absolutely charmed and smitten. The whole marketing hype around Virtual DOM was infectious. The lack of digest cycle issues and other Angular problems went away.
Building apps in React was fresh and exciting; React was a lot easier to grasp and use than AngularJS or other options at the time were.
I don’t want this to be a “let’s bash React” article because it’s not. This article aims to point out that while React might be the most popular option, it’s not always the best option. The truth is most front-end developers could not give you a valid reason for why they use React over something that might be better suited. The best you will get is, “it’s the most popular option.”
Developers who choose React because of its size and stature very much remind me of the old ’90s saying (or was it 80s?): Nobody ever got fired for buying IBM.
And look, nobody is going to be fired for choosing React in most instances. Unless you’re this guy who wrote about how he almost got fired for choosing React for an enterprise app. Who knows if React was to blame here, but you can’t argue with the facts presented in that post; React can be a time sink if you’re not using something like Gatsby or Next.
But, there is definitely a degree of “bigger is better” when it comes to React, a lot of people choose it because it’s the biggest. It’s where that IBM saying came from, IBM was huge in the enterprise (not so much these days), not because their quality was better, but because they were the biggest. If anything ever went wrong, IBM would get the blame.
We are now seeing a generation of developers graduating from coding boot camps and doing online courses focusing on React, instead of the language behind it (Javascript). This isn’t a new phenomenon, I remember back in the jQuery days where some developers knew how to write jQuery, but lacked knowledge of actual Javascript, not even knowing how to query the dom without resorting to $()
which explains why a lot of old StackOverflow questions from a few years ago about Javascript have jQuery answers.
React has a place and purpose
I have seen some amazing things built with React, this article will not be arguing that it is inferior or incapable of allowing you to build applications. I also want to point out that while this is an article recommending Aurelia over React, many of these same arguments can be made to choose Svelte over React.
I think React has a place in modern front-end development; it shines for smaller projects and things like landing pages, but for a large-scale line of business application and enterprise purposes, React is a world of pain waiting to envelop you.
For projects where you don’t need to maintain hundreds of components, pages, communicate heavily with API endpoints, deal with large amounts of data and complex UI’s, React is a great choice. If you’re building landing pages or simple apps, I would definitely agree that its lighter approach works well for simple to intermediate use-cases.
And once again, I am sure many of you can point me to examples of large React apps as well as companies who are using it. Nobody is saying you can’t build apps in React. You can pop the top of a beer with a spoon or knife, but a bottle opener is better suited.
But, Aurelia isn’t…
I know some of you are going to throw out some of these retorts about Aurelia, so to save you the trouble and some time, let me do it for you:
- “Aurelia’s community is too small” — Nice observation, Sherlock. Yes, it is. The size of a community doesn’t make a framework or library better or worse. If anything, the smaller community is a bonus as you’re more likely to get help from Aurelia core team members when you run into a problem. Can you ask React core developers for help?
- “The documentation sucks” — A valid criticism for Aurelia 1. Some people still in 2021 also think the React documentation sucks as well. Fortunately, for Aurelia 2, the documentation doesn’t suck.
- “Smaller selection of plugins” — once again, a valid criticism. The smaller community and market share means sometimes you’ll have to do things yourself. Fortunately, Aurelia is so easy to work with and works with all third-party libraries (see no VDom) that it’s not that big of a deal. You can even use jQuery libraries in an Aurelia application.
- “Aurelia isn’t popular” — In comparison to other choices out there, Aurelia is one of the lesser known options. Fortunately, not every developer sees choosing technologies as a popularity contest. If it meets your needs, is well-maintained and the technical requirements match the project, isn’t that enough? Do you remember what else used to be popular? Blockbuster, VHS Tapes and handshakes. Times change.
- “There aren’t that many Aurelia jobs” — quite valid. I am fortunate enough I get to work with Aurelia every day, but the job market for Aurelia is small. Do you know what other library has fewer jobs at present too? Svelte. It hasn’t prevented it from being slowly embraced by front-end developers.
Many of these arguments against React apply elsewhere
While this article makes the case for Aurelia over React, many of the same arguments could be made for other technology choices. The three that come to mind are Angular, Svelte and Vue.
To me, Angular and Aurelia are similar when it comes to large-scale application development, particularly enterprise applications. Angular is a decent choice, but it is a lot more opinionated and requires a bit of boilerplate which Aurelia intentionally tries to avoid you needing to write.
Svelte is a promising up and coming lighter alternative, marketed as a compiler, you write code that more closely resembles plain Javascript, it also has some libraries that let you hit the ground running without having to do a heap of research and development.
Then you have Vue. Arguably, we have Vue to thank for concepts like single file components (SFC’s) and it should be praised for offering binding features such as two-way binding. However, with Vue 3, they appear to have gone in more of a React direction. You still get a lot of what you need out of the box, but the introduction of their own React-like hooks seems to blur the lines between Vue and React.
Aurelia is better out of the box, React is too unopinionated
Many developers will tell you that the lack of standards or opinions regarding building React applications is a good thing. In a lot of situations, it’s actually not. React for such a long time (and maybe, still does) marketed itself as the “V” in MVC, just a humble view library.
The lack of conventions or even basic guiding principles of where things should live, what they should be called and how a basic React app is supposed to look like can result in a lot of mess. and paralysis when it comes to even make decisions about what the app should look like or use for basic features.
If you were to follow the official getting started documentation, the resulting app you would have would provide nowhere near the features and functionality that most web applications require. Out of the box, React is borderline useless for most use-cases. Displaying a list of array data? No problem. A CRUD based application with authentication, validation, internationalisation and routing? Not so much.
When is the last time you encountered a React application that didn’t need a router, didn’t require working with forms, didn’t use some form of state management (possible with hooks and context API now), validation and a slew of other crucial pieces of a modern web application or site?
With Aurelia, a basic generated app using the CLI will give you almost everything you need right from the get-go. You get an HTTP Client for working with native Fetch, and you get a powerful validation plugin, a router, intuitive template control syntax and a convention-based approach that gets out of your way and allows you to have more power when you need it.
When you use React, you have to make many choices if you’re going the conventional path. You’ll be npm installing numerous third-party packages, all maintained by separate authors. Because popular React packages with one maintainer underpin the React ecosystem, it’s a house of cards.
- What router package do I use?
- What state management (if any) library do I use?
- Should we use browser native Fetch or a library like Axios?
- Tailwind CSS, Styled Compoents, SCSS or plain CSS?
- What form library will you use? Especially if you’re using something like Redux.
Just answering these questions before work even can take weeks. Everyone on your team who has worked with React will have worked with different libraries and have conflicting opinions based on their experiences. Can you imagine arguing over what router to use?
After you’ve spent all that time researching and validating your technological choices, everyone has to familiarise themselves if you’re working in a team. Not everyone would have worked with all of the chosen packages you have gone with. Another couple of weeks passes, still not much work done.
Then what happens if there a major React release and your chosen library or tool isn’t compatible? You can’t use the new exciting features because a tiny part of your app hasn’t been updated yet. Turns out that the package you rely on is maintained by a single author being paid $0 who has a full-time day job.
Convention over configuration; easier to structure
When structuring a React app, the community can’t decide on a best practice standard. It’s rare two React apps will be alike. You familiarise yourself with one React codebase; the next codebase you work on will be completely different (most likely).
The issue with React is that no two projects are the same. Knowledge cannot be transferred from one project to another. It seems every developer has their own approach to structure, naming and dependencies they choose to build an application. Maybe okay for a side project, but in the real world, like enterprise, not so much.
Is it any wonder that some of the most popular React plugins/libraries are frameworks built around React itself? You have Next.js, Gatsby, and a few other smaller frameworks that aim to address the out of the box limitations of React. Essentially turning React from a view library into a framework with opinions and standards.
The beautiful thing about Aurelia is that it is convention over configuration as a default. If you want to override something, Aurelia gets out of your way. But, for most use-cases, the conventions are actually what you want, and it saves a lot of time.
It’s also not rare to encounter production Aurelia apps that feel familiar to one another. Stepping into different React codebases is like travelling to different countries; the food tastes different, people might speak a different language, the speed limits are in a different unit of measurement.
Aurelia offers stability
Due to the lack of batteries included in React, you need to make your own technological choices when it comes to several aspects of large-scale applications. But, here is the problem: The React ecosystem is constantly changing, new patterns and approaches emerge, especially if we are talking about Hooks. New libraries emerge for routing, state management and who knows what else.
With Aurelia, you can choose it knowing that because it gives you many features you need out of the box, you don’t have to worry about outdated packages or being left behind when a new major release of React comes out. It’s this kind of stability that matters in large projects (>2 years) and the enterprise.
Not everyone has the luxury of being able to continually update and change their apps every six months when something new and shiny comes about.
Aurelia is a friend to all
Because Aurelia is enhanced HTML, Javascript and CSS, it plays nicely with almost every front-end package you can think of. Need a charting package or want to use a legacy jQuery plugin your company built 10 years ago nobody is brave enough to rewrite? The lack of Virtual DOM or any other needless abstractions means you can interact directly with the dom, mutating it and even creating component wrappers around it.
Want to bind to nodes of an SVG element and modify them on a molecular basis? You can do that and not have to worry about weird side-effects or side-stepping abstractions.
Want to get crazier? You can even use other frameworks or libraries inside of Aurelia. I’ve integrated React inside of an Aurelia app before, just because I could.
Aurelia scales more efficiently
I don’t mean bundle size or performance when I say scale. I refer to Aurelia’s ability to accommodate large-scale applications while managing to keep things clean and structured. At my day job, I am working on an Aurelia application that is three years old. One of our component folders contains 56 components, another contains 32 components.
In total, we have well over a couple of hundred components in our style guide underpinning a suite of applications that can four (and counting) applications all using shared components and configuration logic. Even with a codebase this big, the longest I’ve seen it take a new developer to become familiar enough with our codebase that they start contributing code, it was one week and they had never worked with the front-end before.
In that time, there has never been a single update released by the Aurelia team that has required going and changing anything inside of my application en masse. They got the design right the first time, unlike React, which still feels like it’s an experiment (classes, function components, hooks).
I understand that structure and scale concerns are somewhat personal, but I have yet to encounter a clean large-scale React codebase. It doesn’t mean they don’t exist, but I have yet to see one.
Reactive binding is awesome
Despite React marketing the Virtual DOM as this amazing performant thing, the Virtual DOM is actually slow. When you really look at what the Virtual DOM is, it is an over-glorified dirty-checker, and it uses a lot of memory. Profile a large React app, and you’ll see first-hand how great the VDom is.
In Aurelia, there is no Virtual DOM. Svelte and Elm’s same thing is a reactive binding approach to displaying data and keeping it in sync. Values change when updated, and the entire application doesn’t get re-rendered, no expensive in memory comparisons. You don’t have to worry about re-rendering your entire UI or considering how your properties are passed or referenced.
This is the direction a lot of non-React libraries are taking now.
Aurelia makes working with forms easy
If you want to experience immense pain, try working with forms in React. Working with forms (especially if you want validation) will make you curl up into a ball and furiously mash the keys on your mechanical keyboard. Forms and React are a form of torture.
Working with forms is so bad that there are libraries out there to make working with them easier. The most popular one right now is React Hook Form. I understand working with forms even outside of React can be painful, but React somehow makes working with them worse.
Form inputs are perfectly capable of holding their own ephemeral state. Still, side effects and all of that functional programming nonsense React developers fight so strongly against means you have to use callback functions and references to work with input values.
In Aurelia, if you want to watch a text input for changes and have the value update in your view model, you do this: <input type=”text” value.bind=”myVal”> it just makes so much more sense: no callback functions, no need for references.
Aurelia has nicer syntax
Filed under: JSX is an abomination.
Some developers really love the JSX syntax that React heavily promotes, but even when I started out using React and used it for a while after, the JSX syntax always left a bad taste in my mouth. This is a personal opinion here and not necessarily a fault of React, but it feels so far removed from HTML.
Take this snippet of code from the official React docs on working with forms:
render() { return ( <form onSubmit={this.handleSubmit}> <label> Name: <input type="text" value={this.state.value} onChange={this.handleChange} /> </label> <input type="submit" value="Submit" /> </form> ); }
Now, here is how you would write something like this in Aurelia:
<form submit.trigger="handleSubmit()"> <label> Name: <input type="text" value.bind="state.value" /> </label> <input type="submit" value="Submit" /> </form>
Because form inputs are two-way data-bound, you don’t need a change callback to know when the value changes. Binding to native events is intuitive and familiar submit.trigger
will call our handleSubmit
function when the form is submitted.
Better still, let’s compare how you iterate over data in a component in React and Aurelia. First, the React way of doing it:
function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li>{number}</li> ); return ( <ul>{listItems}</ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') );
The Aurelia way of doing this is purely inside an HTML-only component (no view model). In a real application, your numbers array would be inside a view model and accessible to the view, but we inlined it for this example because we can.
<let numbers.bind="[1, 2, 3, 4, 5]"></let> <ul> <li repeat.for="n of numbers">${n}</li> </ul>
Two-way data binding in Aurelia is awesome
In Aurelia, you have multiple binding modes. By default, a lot of the bindings you use will be one-way. However, you can also have two-way bindings (the default for forms), allowing you to keep bindable values in sync across your view and view models.
As you saw in the previous form examples, React requires you to use a change callback and other unideal approaches to getting updates from a form. There is a reason that both Svelte and Vue also have two-way binding because, despite what you’ve been told, it has very few downsides (unless you’re trying to use them with state management).
Aurelia uses classes (which aren’t bad, btw)
A lot of React developers well and truly drank the anti-classes Kool-Aid. With Facebook introducing Hooks to stop supporting class components one day (probably), many developers used this to fuel the anti-class rhetoric that existed long before React did.
If you’re not using “pure functions” in React, you’re sinning. Funnily enough, nobody can give you a proper explanation of why classes are bad outside of some theoretical academic environment or made-up scenario that hasn’t been a thing since 1998.
In Aurelia, everything is a class (much like it is in Angular), and many developers have been deploying Aurelia applications since 2015. The sky hasn’t caved in, and a mythical anti-class deity hasn’t come down from the clouds to scorn the Earth and leave only pure functions behind.
Do you know how many problems I have encountered in my Aurelia applications that were caused by classes? Zero.
Classes are natural namespaces. And yes, while the anti-OOP/class crowd have some valid arguments against classes, there are none to outright shun them. When used correctly, classes can be a beautiful way to encapsulate functionality instead of a bunch of singular pure functions you have to follow one by one to know what is going on.
This is what a basic custom element looks like in Aurelia:
export class PeopleListCustomElement { } <ul> <li>John Smith</li> <li>Jack Black</li> <li>Bob Jones</li> </ul>
Then you can reference it like this:
<people-list></people-list>
Aurelia has HTML only custom elements
One of my favourite features in Aurelia is the ability to create HTML only custom elements. When I say HTML only, I mean a component that probably more closely resembles a Web Component, except there is no Javascript whatsoever, no view model or inline JS.
Say you want to create a loading component, but it’s CSS only and doesn’t have any Javascript. Here is what it could look like. You would save this as something like app-loader.html
— in Aurelia 2, you don’t even need to write the template tags.
<template> <p>Loading...</p> </template>
Then, reference it like this:
<app-loader if.bind="isLoading"></app-loader>
Aurelia is more familiar to .NET developers (dependency injection, etc)
There is a lot of overlap between Aurelia and .NET. Both utilise similar concepts (views and view models), classes, and so on. I have seen first-hand how easy it is for .NET developers to get familiar and up and running with Aurelia numerous times.
This is where React begins to form some cracks. The lack of familiar concepts and, more specifically, the lack of dependency injection makes it quite an incompatible pairing with modern React, which fights against the use of classes and has no DI (amongst other familiar comforts).
Some will argue with you quite strongly that you don’t need DI, but many will beg to differ (myself included).
Did you know that .NET ranks consistently for being the most loved framework according to the StackOverflow developer survey? In enterprise environments, .NET is one of the most popular options around.
Aurelia is faster to build with
I will issue a challenge to anyone reading this who wants to take me up on this. I bet given a brief for an application of some sort, I could build something in less time it takes for someone to build that same application using React. I have prototyped entire features and apps in a day or two with Aurelia, not having to worry about certain technological choices just makes it faster to work with.
While you’re busy deciding what router or state management library to use, I am already writing code.
Make your own choices
At the end of the day, I am just another biased developer with an agenda. You should always make informed decisions, never blindly follow trends or choose technology based on a blog post or how many stars it has on GitHub. It’s time front-end developers started to make informed decisions again.
*Laughs in VueJS*
Thanks for that. I’m nearing V1.0 of my first React app. Now that I’ve experienced it first hand, I can see that there’s merrit in many of your criticisms. It’s … ok … however I’d be hesitent to use it for something large.
I’d be interested in you doing a comparison between Aurelia and Svelte.
@Damien
React definitely shines for smaller projects with fewer moving pieces. I do find the premise of JS code with minimal dependencies alluring, but definitely for larger projects where you need to maintain so much more, it can be a cognitive and maintenance nightmare. That’s not to say you can’t make React work for most uses (because some companies do). It’s just that every time I have seen a large React codebase, the company has had to create their own tooling and development approaches to overcome the inherent limitations with it.
Aurelia and Svelte actually have quite a lot of similarities, they differ in certain aspects, but many of the same underlying principles are the same. They’re both closer to the metal, both avoid unnecessary abstractions between code and the browser, leveraging browser APIs for certain things. I really like Svelte, not a fan of the faux-Moustache syntax, but I very much like it.
Funnily enough, I have a post I’ve been working on comparing two identical apps built in Aurelia and Svelte. It’s not live yet, but here is the code for both of them:
– https://github.com/Vheissu/svelte-todo
– https://github.com/Vheissu/aurelia2-todo
Thanks Dwayne, I’ll have a look 🙂
for everyone that comes across this article and looking to draw their own conclusions, this is more representative React sample code for iterating over a list and generating page elements:
{[1, 2, 3, 4, 5].map(i => {i})}
the given example was misleading for a couple reasons
1) breaking out that NumberList functional component isn’t necessary and would’ve required additional code to do in Aurelia as well and
2) the ReactDOM.render piece is done ONCE PER APP at the outermost bootstrapping layer which Aurelia requires an equivalent (https://aurelia.io/docs/fundamentals/app-configuration-and-startup/)
{[1, 2, 3, 4, 5].map(i => {i})}