All JavaScript frameworks are terrible

This article was originally written as a partnership with another one praising the virtues of all frameworks. It was intended to show in large part how easy it is to “spin” frameworks without ever being inaccurate. However, on working on both of them it was obvious to me that the other article was stronger, and I let this one wither on the vine. I’ve decided to finish it off and publish it, but need to reiterate. The other is the better article.

JavaScript frameworks are amazingly subjective things, and we like to argue about them regularly. In order to save you time and provide valuable ammunition against all the people who are relentlessly wrong on the internet, I have written here a solid list of why you absolutely shouldn’t use any framework.

I should note that of course I don’t mean you shouldn’t use a framework. Obviously the choice of a modern development professional is that you should use a framework to make your application consistent and well structured. You just shouldn’t use any specific one. Because they’re all wrong.


Angular is a self-evidently poor choice. It’s out of date now, and new projects should absolutely not be built using it. This isn’t to say it was ever a particularly great choice. Angular came out of nowhere and became popular by default, rather than because it had particularly great design. It has a difficulty curve better suited to a great roller coaster than a decent framework, and a bunch of weird architectural and terminology choices. What the hell is a $scope, anyway? And what is a directive? What does transclusion actually mean? Meaningless terms that Angular has created. Angular does some things that are fundamentally wrong, such as creating invalid attributes on HTML markup. Even Google doesn’t use Angular for their own apps, like Gmail, and there’s a reason for that.

In fairness, Angular was always a poor choice. Its idiosyncratic code means that unlike other frameworks, it’s not good at implementing an agnostic, javascript solution. Angular code looks unfamiliar to anyone not super experienced with the weird intricacies of Angular itself.

Thankfully, this decision is well made for you – Angular is now quite thoroughly dead, and only legacy projects will continue to be using it. We should be grateful for what Angular has given us, and respect the position it held, but we should be just as pleased that it’s gone.

Angular 2

It’s a pity Google dropped the ball so thoroughly with Angular 2. Angular 2 shares so little in common with Angular that it’s literally in name only. If writing new software, there is little reason to choose Angular 2 over other current options. Strange and unfortunate that so many developers seem keen to jump from Angular to Angular 2 without really considering any other (and better) options.

Some of the choices made in Angular 2’s development are inexplicable. A perfect example is its use of Case Sensitive HTML, which blatantly violates HTML standards and requires them to implement their own parser. Angular 2 templating is a tattered mess, a syntax soup that has no meaning to anyone outside Angular’s own knowledge base.

<li *ngFor="let post of posts">
<a [routerLink]="['/posts',]">{{post.title}}</a>

Anyone looking at this from a standard Javascript background will be completely baffled. What’s with the asterisk? What’s with the square brackets? Is it an array? Is the first thing in brackets the same as the second?

TypeScript is another odd choice. How many JS developers actually know TypeScript, compared to JS? Very few. Why not write in the language they already know instead of forcing them to jump through hoops, if not reducing your employee talent pool?

It should go without saying that we’re going to ignore the directive to call it AngularJS. When comparing frameworks and one is called Angular and the other is AngularJS that’s a recipe for the hot mess of documentation the Angular community is currently suffering. It’s near impossible to tell at a glance whether the docs you’re using are current for Angular 2 or 1, and the decision to keep this ambiguity through the already promised Angular 3, 4, 5, etc is moderately disastrous.


Aurelia’s biggest disadvantage in the market is an obvious and unfixable one. No one uses it. Searches for various frameworks on Google Trends show Aurelia’s popularity static as well as very low. It has little to no community support, which means minimal help and no Stack Overflow questions covering every case.

That isn’t to say community support is Aurelia’s only flaw. The choice of JSPM as a build tool is an inexplicable one, the worst of both worlds. It’s not supported by any other project, unlike more standard builds using typical toolchains of Webpack, browserify, Gulp, etc.

Rob Eisenberg was for a time a developer on Angular 2, and it shows that heritage. Essentially when he left Angular 2 he just went and updated Durandal to be a bit more contemporary and that’s what Aurelia ended up being. It’s not terrible, but there is no reason to recommend it over better supported and better structured frameworks.

That said, it’s hard to tell if Aurelia is a framework, or a business model. If you go to the website, the most prominent top links are to support, training and consultancy services. “How to Aurelia” is a lower priority. When you do get to the docs… to say they’re minimal is an understatement. How do I link to a route? Seriously, I can find a bit about it on Stack Overflow but nothing on the official docs.

What’s particularly strange about Aurelia is that its design methodology is that it “doesn’t get in the way” of your coding. But it doesn’t really make much effort to help, either. Everything has to be explicitly included, everything has to be explicitly written. Aurelia’s hands-off approach seems to be simply contradictory to the point of using a framework in the first place.


React is great. It’s nice and easy to use!

That’s what the back of the box says and it’s true up to a point. The problem is that React itself doesn’t actually solve any real problem. It’s not a framework. It’s just a library. If all you need is a component based ViewModel then off you go. But it’s not. To solve any problems that aren’t trivial you’ll have to build some ridiculous stack of React, some sort of router, the absurd state pattern that is Redux, whatever the hell Flux is supposed to be… You even need a special package to integrate React with Redux. Like there’s any other usecase. You’ll have to tie that in with some kind of Babel Webpack sandwich if you want to put on your big boy pants and do this in ES6.

Before you know it you’ve fulfilled that old statement: “If you don’t choose a framework, you’ll end up building one.” Now you have to maintain a build pipeline, as well as an increasingly complex API as all the functionality you’ve stacked teetering on top of itself just hopefully all keeps working together. It seems to appeal to the NIH Syndrome developer who really wants “control” that extends through micro managing a complex build system. And just hoping that any open source library with the features you need happens to support whatever module format you ended up with.

That’s not to say React itself is flawless either. Growing up as a young programmer I learned a few pretty basic rules. One is don’t forget to put a WHERE clause when manually updating a client’s product SQL table just before deploying it. The more relevant one is “don’t put markup from your methods”. React plainly violates that, and supporters blithely say “Ohhh, the old rules just don’t apply to React!”. Yeah. They do. Separation of concerns and code quality actually do matter. Testability matters. Code completion and syntax highlighting matter.

The typical response of React users is to say that because React’s specific domain is the view, this isn’t Separation of Concerns, it’s “separation of technologies” and doesn’t count. This is merely a highly specific definition of the necessarily vague term concern, and not an actual response. The technologies are the implementation of the concern. Dumping a load of HTML in your javascript functions should make you feel dirty.

React’s attitude of just wiring everything up and wrapping it in enough duct tape that it works isn’t necessarily particularly disciplined and professional. JSX is one of the single worst things that has ever been done by any JavaScript framework, and anyone who uses it should feel the stinging disappointment their family has for them.

Of course, only one of these frameworks has a license that means your right to use the framework can be taken away if there is a patent or legal issue. The original wording basically said Facebook could revoke your license if they don’t like your hair but it’s been clarified now to specify when exactly they can revoke it. Revoke your license. Just ban you from using React. Many React supporters will happily tell you that it’s ok, that’s not going to happen. But “don’t worry it’s fine they’d never do that” has about the same legal standing as “Finders Keepers Losers Weepers”, or “Am I being detained”.

React at one point in its life was a breakthrough. It was for some time by far the fastest framework. Or library. Or whatever. It pioneered the Virtual DOM, forcing every other framework to compete. And they did. And they did it better. React isn’t the fastest anymore, libraries like Vue2 and Inferno kick its ass in performance.

In fact, React isn’t even the best React anymore. Preact is a near identical drop-in replacement, using the same API, but greater performance, a much smaller download, and a license that doesn’t need to be signed in the blood of a child.

No one can doubt that major companies are making amazing things with React. But those companies can pay a team of engineers to live on the cutting edge and keep the duct tape in place.


Ember has managed in just a few years to go from being the third most popular Javascript framework to being the fourth most popular Javascript framework thanks to the release of Angular 2. With tiny market share comes tiny relevance, with few jobs specifically listing Ember skills unless they say “Ember, Angular, React, Backbone or whatever idgaf”.

Ember has its own way of doing things every time. It doesn’t use the sort of custom build stack popular with most developers these days, opting for its own “Broccoli” build tool instead of industry standards like Webpack. Despite its strict “MVC” pattern it doesn’t actually require controllers, and they’re being phased out. Or not. The actual role of what any other framework would call a controller is called a route. Of course, there are also controllers. You can put actions and events into the controller. Or should they go in the route? Yes! Who knows!

Ember is by far the most “frameworky” of frameworks. If there’s a flaw in frameworks, Ember has it the most. Download size? Ember’s the biggest. Opinionation? Ember has it in spades. Cognitive load? Ember has the largest API footprint and steepest learning curve of any framework. Performance? Ember is the slowest.

And by a lot. Even the latest Ember update with Glimmer 2 is positively plodding. Of particular concern, the memory footprint and initial download are dire — which has direct impact on especially mobile usage, and leads to a particularly slow time to interactivity.

Some of Ember’s ideas are good in theory. Convention over configuration is a nice idea. But Ember has basically no flexibility. Its conventions are rigidly enforced. Part of this enforcement is done by its CLI. Again, CLI tools are a good thing. CLI tools for JavaScript frameworks are becoming increasingly popular – Angular 2's is a direct fork off Ember CLI, and Vue, React, Meteor, and Aurelia also provide these tools now.

Two key differences between those are that in every other case, the CLI tool is a handy feature. Only in Ember is it mandatory. And also that Ember’s CLI doesn’t work in Windows. Yes, you heard correctly. Though not impossible to hack into working, EmberCLI’s Broccoli build system is one of the few Node tools that simply won’t work in a command line on the most popular OS in the world. This is because Broccoli needs a “compiler” for some reason. To write JavaScript.

Which is weird because it’s barely JavaScript at all. Ember goes out of its way to make sure it’s never exposed to anything resembling dirty dirty JavaScript objects. Everything is wrapped up in Ember objects. Even something as simple as a computed property has to be wrapped up.

fullName: Ember.computed('firstName', 'lastName', function() {
return `${this.get('firstName')} ${this.get('lastName')}`;

Explicitly defined computed dependencies as a bonus. Every major entity type is a subclass. They all return default Ember.Component.extend({.

Note also that Ember. that is all over the place? This is a single import of the entire framework. It means that no static analysis is possible, no optimisation is possible, no tree-shaking.

It should be noted that unlike competitors, Ember has no corporate backer. Angular has Google in its corner. React is promoted and used by Facebook. Ember lacks this level of support.

It’s not fair to dismiss Ember as having no market relevance. It’s for “ambitious” applications. There are huge companies using it, and it’s fairly well regarded in the bigger end of the market. Twitch, Apple Music, PlayStation Now, LinkedIn, Heroku, Netflix. They all use Ember apps for either public or internal projects. Interestingly, League of Legends client interface is actually a very complex Ember app. But if you’re not dealing at this scale (and with these resources) then you probably don’t need such a complex and all-consuming framework. Especially if some of your users have a phone.


Vue is a framework not many people have a lot of exposure to. It’s particularly prevalent among users of PHP’s Laravel framework, which promotes it heavily. This makes a certain amount of sense for Laravel. Vue is a small-api-footprint library intended to create JavaScript components on existing backend driven applications. That said, I’m not sure backend frameworks should be dictating or even suggesting frontend implementations. In particular, Laravel’s approach recommends serving the front-end app from the back-end. This paradigm is fundamentally flawed. In my opinion, your backend and front should not be dependent on each other, nor linked in any way. Laravel naturally promotes a “sprinkles of JavaScript” approach, where the heavy lifting is handled by the backend framework. This again is a flawed approach leading to death by a thousand razor sharp sprinkles. It makes sense if you think of this from the backend-first worldview that Laravel inevitably has, but far less if you take a more objective view about what makes the best end result for users.

None of this seems particularly relevant, but Laravel’s dubious support is about the only reason this “framework” has any traction. The scare quotes aren’t meant to be dismissive. Vue isn’t technically a framework, though that term has a pretty squishy definition. Vue is more accurately a library, like React. There’s nothing wrong with a library with a focused goal, but Vue doesn’t stay like that. It has the same issues as React in that sense.

Of course, whether it’s a real framework for complex applications or a simple library for componentising view elements depends on what criticism Vue users want to avoid at any given point. A friend of mine refers to it as Schrödinger’s Framework.

It’s not all subjective. Vue has the single worst CLI tool of all of the frameworks. They’ve looked at the incredible productivity a good CLI provides and decided that’s not for them. Even the tiny functionality provided is implemented badly, providing no reasonable default and necessitating a bunch of research and poorly informed decisions. Forcing users to make decisions they shouldn’t have to for no actual gain seems to be the design pattern of Vue. There is little or no support for testing (nor the faintest bit of discussion of it in the community). There’s no consistency to apps, which may vary drastically in functionality and even language. Some of Vue’s ideas like “Single Page Components” enthusiastically violate principles of Separation of Concerns with a casual dismissal. “It’s so convenient to have all my crap in one place” is a good idea at first, but becomes awkward and unwieldy with anything larger. Which is the definition of an anti-pattern.

Vue’s recent efforts sell the framework (or whatever) are calling it “progressive”, handwaving drastic architectural changes in favour of an easily demoed approach no one needs, to problems that aren’t hard to solve. Vue consistently sells itself as being easy to get started with, showing an “old-school” CDN script tag, but then discusses complex advanced functionality with no mention at all of the tooling changes required.

Unfortunately Vue seems to appeal to a certain segment of the market — most particularly people who have never used a framework, or only ever used Angular and seem to find Vue an improvement. That Vue succeeds at this shows just what a low bar it is.

Meteor JS

Meteor is a marvellous example of the contrast between the promise, and the delivery. In theory it offers full-javascript-stack goodness, able to seamlessly run realtime applications.

In practice… realtime is a big selling point, and it is possible. But in order to use the realtime functionality you need to use something called Distributed Data Protocol. DDP is an old-school RPC system that hooks into WebSockets. If you’re not familiar with RPC, or remote procedure calls, they essentially involve running a function from one system to another. The syntax and approach is very much like SOAP if that means anything to you. And the reason I keep having to check that these terms are know is that for the love of God no one does this any more. Industry standards like REST? Meteor scoffs at them. This of course means your data is going only to your single client. Your API can’t be generic or used in multiple contexts. You can’t treat it separately or test it separately, and authentication is difficult. You can’t use it for a mobile app, or let people have access.

Starting with Meteor the problems begin nearly immediately. One of the fundamental features of a framework is a router. What is Meteor’s? That’s a complex question. It actually has several routers, two major ones (Iron and Flow), with quite different capabilities. Flow is particularly awful, throwing all of its functionality into anonymous functions directly in the route handler. Backend frameworks like Laravel typically allow this as an option, but also support a proper controller to make code more discrete and maintainable. And yet it’s Flow that seems to be the standard.

Meteor recently made a change to allow it to use React or Angular. Which is interesting but… why would anyone want to do that? The point of Meteor was always that it was a consistent and coherent interface. If I’m using React as a frontend why am I using Meteor as a backend then?

Another proclaimed feature is that everything’s in JavaScript.

Atwood’s Law: any application that can be written in JavaScript, will eventually be written in JavaScript.

Does everything really need to be JavaScript though? Because for all homogeneity is nice some of the JS solutions are… not the best. Let’s take a look at MongoDB, for example. Mongo is Meteor’s idiomatic persistence layer. Which is an odd choice for a grown-up framework. Sure, MEAN makes the same choice but it’s a training-wheels stack for children. MongoDB has the same issue any noSQL database has, which is that most application data is relational and in actual use a relational data is vastly easier to use, more efficient, and more reliable. MongoDB is great for prototyping applications, especially where the schema isn’t stable, but for production applications it’s a much harder sell.

And that’s without mentioning the recent exploits that left MongoDB servers all over the internet compromised recently. Ok, mentioning it a little bit.

The promise of code sharing is nice. Code can be used across the front and backend without duplicating requirements in different languages. But in practise the use of this is limited. Aside from the admittedly valid case of document models and cross-cutting concerns such as logging, the front and back end are doing such different things that there’s relatively little value in sharing code.

One of the most interesting things about the current generation of frameworks is that they provide implementations of server-side rendering or universal applications. Vue has a particularly elegant model, and Angular2’s is a bit more effort than you’d think but you can do it. Aurelia has taken large steps towards it and it’s listed as one of the major features for this year. Ember’s approach is probably the best, a single command install, and is in beta.

Meteor by contrast has this seems to be a complete non-starter. Information is patchy. The most current comes from the all-too-appropriately named “Meteor Hacks” website, and documents an unofficial approach in 2014. Other articles outright reject it, while also getting the actual definition completely wrong. Given time and work, a Meteor site can be made to serve as an SSR version, but it’s by no means the solved problem of other frameworks. Which is odd considering that would seem the sort of benefit you’d get from using it in the first place.


I feel like people are going to be mad about this article because I misrepresent their favourite framework. The reason they should be mad is because I misrepresent all of the frameworks. It’s easy to read something that confirms your perceptions and agree with it.

One of the reasons I didn’t complete and publish this earlier is a simple and inescapable fact: I don’t believe a lot of it. I’m an Ember developer primarily. I like Ember’s patterns and approaches and its level of integration and opinionation, here phrased as a weakness, is one of its greatest strengths. I do wish it was faster and smaller, though. Any rational assessment should accept both strengths and weaknesses.

The reason I wrote this article was initially to point out that nearly every trait or aspect of a framework can be phrased as either a negative or a positive, depending on your pre-existing views. Whether Angular 2 is a integrated solution or an unwieldy monolith is far more in the eye of the beholder than an objective truth. It’s easy to move goalposts, make apples to oranges comparisons, or misconstrue something’s intent. It’s easy to do without even being malicious about it.

And the reason I wanted to point that out is an “article” that keeps coming up. This is a page on the website, comparing major JavaScript frameworks with Vue. I personally think that a page about VueJS written by VueJS developers on the VueJS website contains an inherent bias that should be considered. Yet I’ve been criticised for the suggestion.

I could write a whole other article about that page. But the point is that while none of it is objectively “untrue”, to the best of my knowledge none of my article is really false either.

I was considering – and still am – putting up each framework as a separate article, combining the good parts of it with the criticism of everything else. What can be a more compelling article than one that confirms all of your opinions simultaneously?



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Matt Burgess

Matt Burgess

Senior Web Developer based in Bangkok, Thailand. Javascript, Web and Blockchain Developer.