React Through Ember Eyes: Application Structure and Tooling


Benjamin and Tomster pair programming

In this multi-part blog series, “React Through Ember Eyes,” I’ll introduce the concepts of React.js application development from the perspective of an Ember.js developer. This perspective is useful for any JavaScript developer, no matter their background. Throughout the series, I make no assumptions about the reader’s experience with either framework.

Let’s kick things off by applying an Ember lens to application structure and tooling in React.

React appears as the sprawling forest outside the walls of Ember’s orderly garden. Ember developers who leave their comfort zone to work in React might be heard complaining about the lack of structure and conventions. It is a mixed blessing to have such conventions (also called “opinions”). The Emberista who brings a mental model of a “perfect” Ember App — each file in its correct place, properly named, in harmony — will see anti-patterns and know in their gut that there must be a better way.

I believe that Emberistas make great React engineers for this reason. But only if they can tolerate, or embrace, a level of sacrilege. React is the anti-anti-pattern. It broke all the rules and ran away. React trampled on the central dogma of web development by mixing HTML and CSS into JavaScript, and by breaking the hierarchical and top-down control structures of templates, scripts, and styles.

React shifts our focus to the component. In fact, there is no React “application” in the way that Ember people expect. React is nothing but a collection of components. Everything else is external and optional. The concerns of state management, testing, API requests, and even the pipelines for building and deploying the “app” are all outside of React, and can be assembled by any number of third-party libraries or hand-rolled.

If Ember is an Olympic-regulation swimming pool, React is a rushing river.

The Philosophy of each framework is apparent in their Blueprints

Every modern Ember app begins with ember new. The blueprint is provided by the ember-cli tool. As an Ember app grows, new JavaScript modules, with tests, and templates, are typically generated from the built-in ember-cli blueprints. The result of this is the homogeneity of Ember applications around the world. They all look like Ember apps.

React has no such structure. There is a glut of “starter kits” available, each with a different philosophy or collection of add-ons. The most popular of these is the create-react-app tool, which is maintained by the React team at Facebook. create-react-app is not ember-cli. It drops you into a shiny new React project with most of the complexity obscured from your sight. In this initial state, there is no configuration required. Dependencies and development tools are tucked away inside of the ‘react-scripts’ package. You do get a default serviceWorker.js file and manifest.json, which allows your React App to act as a Progressive Web App or “PWA”. I would like to see this in the ember new blueprint.

create-react-app is enough for some projects, at least for a while. When the project outgrows the training wheels, one simply executes npm run eject. This command performs the irreversible step of revealing all the ugly dependencies and configuration files and dumping them into your folder for you to edit. As of this writing, eject will result in the addition of nine config files, three scripts, and 52 new dependencies in package.json. This “ejected” form is a good template of a default or “conventional” React app. I will use this as a basis of comparison against Ember’s default structure.

At first glance, the files look pretty similar

The two apps have much in common. Both have a, a package.json, a public/ folder, and a config/ folder. React puts its application code in src/, Ember puts it in app/. Ember has a tests/ folder. React puts its test files in src/ with the other code.

Within the app/ folder of an Ember app, we have components/, controllers/, helpers/, models/, routes/, styles/, and templates/ sub-folders. There are a few alternatives to this organizing structure, but with Ember it is much easier to follow the convention than to fight against it. In the React src/ folder there are no subfolders. It is totally up to you to organize your source code how you see fit. There are no module generators or naming conventions like Ember has. Every React app is different.

I actually prefer not to think of it as a “React App,” but as a JavaScript app that happens to use React.js. This is because all of the other concerns are external to React; concerns such as compiling the app with webpack, writing and running tests, making API requests, managing your styles, and even running a development server.

The “Ember Way” can be very empowering

Every Ember app starts with ember-cli. Many React projects do not start with create-react-app. In fact, you can add React.js to any project by just installing the npm library or even adding a link to the CDN-hosted library into an HTML file. This is not possible with Ember. Ember is all-or-nothing. One does not simply “sprinkle” ember.js into an existing project. CDN-distributed Ember has been discontinued long ago because ember.js and ember-cli and the whole Ember application structure are all interdependent.

This probably feels grotesque to a React engineer, but within the walled garden of Emberland, it is empowering.

The “Ember Way” is so commonplace that it is almost invisible. Most Ember projects spend little to no thought on their build configurations (contrast with React & Webpack). Ember add-ons can take advantage of Ember application structure conventions and make assumptions that hold true for nearly every Ember app. Improvements to security, build efficiency, linting, test running, and more can be achieved in a centralized way within the Ember community and propagated outward, via ember-cli versions and blueprints, and gain near-universal adoption.

React apps come in many shapes and flavors

The React community is free to develop and experiment with an astounding variety of tools, libraries, and approaches. There does seem to be more innovation, more pushing of boundaries, and testing the limits of what can be done with modern web application development.

It is not surprising that we see React at the forefront of Serverless implementations, Progressive Web Apps, and mobile apps. To an Ember engineer, this can either be a terrifying or a deeply liberating experience. If you don’t like something, you change it, or hack around it, or build something new. Each company, team, or individual contributor is empowered to become a React pioneer and to find their own pattern. Over time, best practices become codified and make their way up the hill to be enshrined into the most popular patterns: create-react-app, Next.js, Gatsby.js, and the like.

Ember has a built-in test interface; React has Jest

In a create-react-app project, one can use npm run build to generate a build/ folder that is “ready to be deployed”. This is similar to ember build -prod except Ember doesn’t use webpack, and Ember outputs its assets to a dist/ folder. In the React app npm test will run a nice terminal-based test interface, using jest. Jest is unfamiliar to many Ember developers, but it is a great test library from the React team at Facebook. It is not React-specific and can be used with Node or other frameworks. The ejected create-react-app injects a bunch of Jest configuration into the package.json file.

By contrast, Ember comes with a customized QUnit installation, that can be run headlessly in the terminal or in a brilliant browser UI hosted at localhost:4200/tests. Ember emphasizes testing and builds tests into its blueprints. React does not do this. Testing approaches vary considerably from one React app to another.

Both ember-cli and create-react-app will set up eslint for your project. Ember includes npm scripts for running the linter on JavaScript as well as on handlebars (.hbs) templates. Create-react-app does not do this, and you need some manual work to get it to run. You cannot customize the linting rules or plugins at all unless you eject from create-react-app.

Ember & React both have a fast reloading development server

The create-react-app project configures npm start to run a webpack dev server, open a browser to localhost:3000, and watch for file changes to update via Hot Module Reloading (HMR). This is extremely similar to Ember’s npm start or ember serve which runs a development server at localhost:4200 and uses livereload to update JavaScript and HTML changes while allowing CSS changes to update in place without reloading the page. One difference is that the mechanics of the ember server are obscured within the ember-cli tool, while the webpack dev server is out in the open to be debugged or tinkered with (after “eject” of course). One can make a good argument for either of these paradigms.

Ember-CLI stays with you; create-react-app lets you go

Ember-CLI does not have an analogy to the eject of create-react-app. You can continue to update Ember, ember-cli, and your application boilerplate (using ember-cli-update) with each new version. A create-react-app project can be easily updated as long as you don’t eject it. Once it is ejected you are on your own to manage everything yourself. This, more than anything, is emblematic of the “React Way” as opposed to the “Ember Way”.

An Ember developer who is striving to understand React should not search too hard for dogma and guardrails but should embrace the freedom of possibility. React puts its developers in the driver’s seat to go wherever they please with the tools available. Even if that means going off-road into uncharted territory.

DockYard is a digital product consultancy specializing in user-centered web application design and development. Our collaborative team of product strategists help clients to better understand the people they serve. We use future-forward technology and design thinking to transform those insights into impactful, inclusive, and reliable web experiences. DockYard provides professional services in strategy, user experience, design, and full-stack engineering using Ember.js, React.js, Ruby, and Elixir. From ideation to delivery, we empower ambitious product teams to build for the future.