For the second year in a row the Ember core team is asking for developers in the Ember community for input into what the future of Ember should look like. I gave my thoughts last year and am excited to do so again this year. Developing in Ember and being part of the Ember community are both a joy, and the least I can do is contribute a few ideas on where I think the framework should go. But first, I think we need to be honest about where Ember is and why.
Ember Lost
The “Framework Wars” are long over. Angular self imploded with the switch to version 2, Ember hibernated during the (still ongoing) Glimmer transition, and despite the recent rise of Vue, React has become the undisputed King of Javascriptland. When companies and startups are deciding which UI technology to adopt, the question they ask is “I guess we should use React, right?”. There has been a power law distribution of framework adoption with React on top and the rest significantly behind. This has had an enormously positive affect for React’s ecosystem - lots of jobs, a vast array of tutorials and addons, relevant stackoverflow results, numerous meetups and conferences all around the world, being taught in every coding bootcamp, and of course the joyous feeling of momentum.
Ember Deserved To Lose
The same can’t be said of Ember, which has not really grown at all and is in danger of shrinking into irrelevance. There are a lot of theories thrown around about why Ember lost. Some point out that React is backed by a giant corporation while Ember is independent. Some say Ember is opinionated which irks developers who love to bikeshed. Others say that Ember isn’t bleeding edge which turns off developers who only want to “sling code using the newest hotness”. I think those are all false, and more importantly miss the point. React beat Ember because React is simple and intuitive while Ember is complicated and confusing!
The market has spoken, and by a large margin developers prefer React over Ember. This might trigger some people who love Ember and are attached to the notion of Ember being the best. I used to be one of these people. I had convinced myself of Ember’s superiority by saying Ember is a “batteries included framework”, how it’s “the Apple of frameworks”, how it’s “better than React, which after all is just a view layer”. I would look at all the great things about Ember from the RFC process, to the story around testing, to the magic of Ember CLI, to Ember Data working so well with CRUD APIs, to the incredible people in the Ember Community and think “case closed”. But what I was missing, and what I’m realizing now, is that Ember has a fundamental flaw holding it back. To those outside of Ember, it’s obvious, but for those in the Ember Bubble™ it’s invisible.
Ember's biggest problem?
Components Are An Afterthought
The biggest breakthrough in conceptualizing how single page apps (SPAs) work has got to be the concept of Components. Using them for the first time back in 2015 when I was on a team transitioning an app from JQuery to React was a moment of mental clarity. Instead of sifting through deeply nested divs and JQuery soup in giant HTML files, I could now just take a wireframe from a designer and effortlessly translate it into React Components. The tagline “React is just JavaScript” is cute, but a more accurate tagline would be “React is just Components”.
React, more than any framework, has embodied the philosophy that SPAs should just be a bunch of Components. This simple yet powerful idea is at the heart of why React spread like wildfire and has yet to be dethroned. In React everything is a Component and everything happens in Components. It’s a really simple mental model that maps onto reality quite well, and developers love it. Ember, on the other hand, has had Components for a very long time, but they play a much smaller role than in React.
Want to write a Component in Ember?
In Ember there are two different kinds of Components - legacy Components, and Glimmer Components, which have been promised to be the future for years. In fact the past 3 EmberConf keynotes have talked up the new Glimmer format...but nobody really uses it to this day, so you should just stick to writing the old style for now ¯\(ツ)/¯.
Want to render a Component based on the URL in Ember?
No need for Components, Ember uses Templates for that! Templates are kind of like Components, but use this other thing called Controllers for managing its state and defining its actions. If you still insist on using Components, you could just render a single top-level Component in the template, but it’s discouraged. For years, the Ember Community was promised Routable Components, but Yehuda Katz waved the white flag of surrender to that notion in 2018, forever trapping many Ember developers in an unending loop of sadness.
Want to use Query Params in Ember?
No need for Components, Ember uses the previously mentioned (and semi-defunct) Controllers for that! They have an awkward interface for Query Params that nobody likes, but it's a low priority so nobody is doing anything about it. Sorry, it’s just an Ember quirk that you need to deal with.
Want to load data in Ember?
No need for Components, Ember uses Routes and Templates for that! If you do nothing, Ember will automatically try and load data for you based on the route (whether you want to or not). Otherwise you’ll need to create a file in the route folder with the exact name as the route you specified in router.js
, and define a function called model
to load data. The use of the word model
assumes you are making a simple CRUD application and loading a single model, not an “Ambitious Web Application” which could load multiple models or something else entirely like a GraphQL query. While you can technically load data from anywhere such as Components, it is discouraged to the point of feeling subversive, since loading data in the model
hook is The Ember Way. Oh and also lest I forget, Ember also loads data when you call a model’s relationship from a .hbs
file in an n+1
fashion which is shockingly appalling and poorly documented.
Want to post data in Ember?
No need for Components, Ember uses those infamous Controllers for that! Just as in loading data, you can also post data back to the server from anywhere in an Ember application...but you shouldn’t. Instead you should define create
, update
, and delete
actions on those pesky Controllers and pass the actions all the way down to the Components that need them. Even though Ember says “Data Down, Actions Up”, in the wild there is a lot of “Actions Down” in application code.
Want to show loading and error state in Ember?
No need for Components, Ember uses Templates and Actions for that! You could define a try/catch
function in the Route’s model
hook, or maybe define an error action on the Controller, or try your luck at creating a Template with the right naming convention (I believe it is [route_name]-loading.hbs
and [route_name]-error.hbs
). In this case there is no Ember Way, but there is a lot of confusion!
Want to manage a Component’s state in Ember?
Great - we have a special javascript file just for that which provides some nice separation of concerns from the template file. Unfortunately the two files are about 6 folders away with the Component .hbs
folder naturally tucked in as a subfolder of Templates, since after all Components aren't that important. Get used to spending extra time navigating between files - hope you don’t use vim ;)
A bit tongue and cheek...but the point is that Ember is fundamentally not a Component based framework. Ember has Components, but their role is subservient to a confusing array of legacy concepts such as Routes, Controllers, and Templates. Ember is trapped in the outdated mindset of MVC in a world that has long moved on to Components. When people talk about Ember’s steep learning curve - what they are trying to say is that they have been accustomed to thinking in Components thanks to React et al, and are confused when they can’t use Components as expected in Ember.
To those outside the Ember Bubble™, Ember developers kind of look like this:
So, how does Ember solve this problem?
Ember: It’s Just Components™
Instead of asking “what do we need to add to Ember to make it better” we should instead be asking “what do we need to remove from Ember to make it better”. Specifically I believe that Ember 4.0 should eliminate the concepts of Routes, Controllers, and Templates in favor of Components. Components are the perfect building block to build apps from. They encapsulate business logic that is understood by everyone from designers to developers, and even business people! Components are a universal language, and for Ember to thrive, it must fully embrace them.
My thinking on this is heavily influenced by the best post of 2018’s Ember Call For Blog Posts - EmberJS 2018: Ember as a Component-Service Framework. After reading that post, I felt more confident in my choice of putting as much logic as possible into Components. I’ve also started to relearn React in the past few months which really made me remember the beauty of just using components. Both have made me reach a tipping point where I now believe that elevating the importance of Components is critical to Ember's future.
Here is how it would (roughly) work, using code from the Super Rentals Ember tutorial.
In the router file Components are specified using the arguments path
and component
. Params (and query params) are automatically passed into the specified Component using params
and queryParams
as prop names.
Router.map(function() {
this.route(path: 'rentals/:rental_id', component: 'rentals/show');
});
The .js
and .hbs
files for Components live in the /components
directory, which assumes some sort of Module Unification has landed. Glimmer Components are now the default, since legacy Components have been deprecated. The word data
is used instead of model
because that is a much more accurate and agnostic representation of what is happening (even in JSONAPI, the top level key is data
). Since this is a routable Component, URL params were automatically passed in and can be called using this.params
.
import Component from '@ember/component';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
export default class RentalShow extends Component {
store: service(),
@action
fetchData() {
this.set('data', this.store.findRecord('rental', this.params.rental_id));
}
}
The .hbs
file renders the Component and also allows for Components to have 3 special props using newly reserved keywords: fetchData
, loading
and error
. First, fetchData
is a function that will suspend loading of the Component and its children until it resolves - similar to what a Route’s model
hook does today and what React Suspense aims to do. To make things simpler, all Ember Data models are now NOT async by default so that the data loading happens exclusively in the fetchData
function, and not in some random line in the .hbs
file. This also makes it super simple to swap Ember Data for another library, like Orbit or GraphQL. Loading and Error state are now just passed in as Components and will be rendered depending on the result from fetchData
. Other than that, it’s just a normal Glimmer Component ;)
<RentalShow
fetchData={{action 'fetchData'}}
loading={{component 'rental-show/loading'}}
error={{component 'rental-show/error'}}>
<h2 class="title">{{this.data.title}}</h2>
<div class="content">
<div>
<img src={{this.data.image}}>
</div>
<div class="detail-section">
<div class="detail owner">
<strong>Owner:</strong> {{this.data.owner}}
</div>
<div class="detail">
<strong>Type:</strong> {{this.data.category}}
</div>
<div class="detail">
<strong>Location:</strong> {{this.data.city}}
</div>
<div class="detail">
<strong>Beds:</strong> {{this.data.bedrooms}}
</div>
<div class="description">
<p>{{this.data.description}}</p>
</div>
</div>
</div>
</RentalShow>
In this hypothetical example, Ember has no more use for Routes, Controllers or Templates. Everything happens in Components, in a way that is extremely easy to understand, and gives up nothing. Quibbles about the details of every choice I made in the example code aside, everything we love about Ember is maintained, while superfluous concepts are eliminated. Ember has half the surface area to learn yet substantially better ergonomics to develop in. The only thing we have to give up to reach this Component Utopia is the past, specifically Ember’s past use of Routes, Controller and Templates. It’s a small price to pay to open Ember up to the wider world of Component-centric UI developers and I sincerely hope this happens.
Make no mistake: Ember is at a crossroads. It needs to grow or it will die. It can’t grow unless it attracts more developers. It can’t attract more developers until it meets them where the are at - which is thinking in Components.
My dream is that this becomes a reality, and not just a quickly ignored blog post. I’m imagining the March 2020 EmberConf keynote in Portland. Yehuda and Tom are walking up to the stage, Tom in his signature suit, and Yehuda wearing an Ember hoodie. Jokes are told, memes are spread, and finally they get to the heart of their presentation. This year it’s not a cheerful retrospective on the previous year with a dash of vague directional hints at multiple possible futures. This year they have one clear message: Ember is now a Component based framework.
While that dream is possible, the Core Team won’t do this unless they hear from more than just one person shouting into the wind on their blog. If you’ve read this and agree with the broad strokes of what I’m saying - speak out, let the Core Team know you also feel this way. I’m hoping there are more than just dozens of us, and that with a ground swelling of support, our message is delivered and change happens!