Modern 2020 React stack, or how we switched away from Angular
Our open-sourced frontend stack and the story behind it
By Clément Déon, Frontend Architect, and Luke Hennerley, Lead Application Engineer at Sidetrade
The essentials
Need a solid React stack for your next enterprise-sized application? We’ve setup everything you need to get started into a Github repo available here:
It’s built with:
- 🏗 Boilerplate: CRA with Typescript
- 🔀 Routing: React Router
- 🧩 State Management: Redux
- ✨ UI Library: Ant Design
- 💅 Styling: TailwindCSS and Styled Components
- ✅ Forms: React Hook Form
- 🌍 i18n: LinguiJS
- 📬 Http Client: Axios
- 👌 Testing: Jest with React Testing Library and Cypress
- 💙 Code Style: Prettier with pre-commit git hook
This is what we’ll be using at Sidetrade for our new frontend projects.
Want to know what made us choose these libraries above others? This is the story of this stack.
Intro — our R&D culture
When working in Web Development, one question often comes back to mind: what is a viable, modern, extensible and full featured Front end stack that can support one’s ambition in the long run?
Around 2 years ago at AI firm Sidetrade, we made the decision that Angular was the answer to that question. It made sense back then, we had one of our main products written with AngularJs, and while it had its flaws, we were still pretty happy with Google’s vision of web development. Angular had become stable enough, and the future of the framework looked promising, so we embraced the solution and it is now at the core of one of Sidetrade’s main products (we migrated most of the AngularJs code to Angular during that time).
Time has since passed, but our culture of innovation remains and goes from strength to strength as we grow. And lately, new business developments at Sidetrade gave us the opportunity to reassess that decision at the scale of the group. So, we took it! We have the culture, the freedom, expectation and encouragement to try new things and keep R&D moving forward.
Recruiting and React
Right now, at the very least in the Paris area, frontend developers willing to work with Angular are scarce. From our 2019 recruiting campaign, we would say that 70% of the frontend developer candidates we saw branded themselves as React developers, 20% as Vue and 10% as Angular. Being in that situation makes recruiting hard.
And, without calling it a grief, after working with Angular for some years we started seeing some limitations that we would be quite happy to get rid of today. To give a couple of examples:
- We loved the all-in-one aspect of Angular at first. But it comes with a price. And trying to go where Angular does not want you to go is often a tortuous journey. We are looking for a bit more flexibility in our next stack.
- The overall heaviness of the framework. We should count how many hours we’ve lost fixing the module imports and declarations in our Angular apps but that’s probably more than what we signed up for at the beginning.
So we decided to give React a proper go, and see if we can come up with a viable competitor to our current Angular stack. One of the main downsides to using React was the ‘un-opinionated’ aspect of the library. If you want your R&D of 50+ people to use something, you have to make it easy for them. That means we need to create our own framework around React that everyone can easily pick up.
How we evaluated — meet the hackathon
We spent some time looking at all the possible solutions for each part of our soon-to-be framework, and we narrowed it down to 1 or 2 options. We then organised a 2-day internal hackathon where we gathered 5 developers, with various frontend development expertise, to implement the same application using 2 different stacks.
Basically: fetching data from an API, displaying the results in a nice way, adding new data and making the whole thing available in both French and English.
Now, our team A and team B got to play for 2 days with:
The aim was to evaluate the overall developer experience for each library: how easy is it to pick up? Is the documentation clear enough? Is it easy to customise? Is it powerful enough for what we want to do with it? That was a fun exercise to do!
The results (and rationale for each choice)
After 2 days of hard coding and vigorous debates, we managed to agree on something that everyone felt comfortable with. We’ll try and explain here why:
Routing
🥇 Our choice: react-router
Let’s start with an easy one. react-router seems to be the de-factor solution for client-side apps. reach-router and react-router are meant to be merged into a single project so we’d recommend sticking with react-router for now while keeping in mind the upcoming API changes (the v6 is being released as we speak). Read more at https://reacttraining.com/blog/reach-react-router-future/
That’s for Client Side Rendering. We didn’t add libs like Next or Gatsby into the equation as we don’t have a need for SSR apps at Sidetrade. But definitely consider them if you do!
State Management
- Redux
- Mobx
- unstated-next
- Graphql with Apollo Cache
🥇 Our choice: Redux
State management in a React application and in frontend development in general is an eternal discussion. And rightly so! It is arguably the hardest part to wrap your head around. Going into 2020 the landscape still seems a little bit fractured, but we could divide the different options into 3 categories:
Handling your own state management using Hooks & Context
Simple, quite easy to understand, and it works just fine. Definitely a viable option for a small-to-medium app but probably too “low-level” if you need more state management features for a large app.
Using a state management lib
There are definitely plenty of choices here, but let’s put the spotlight on these 3:
By far the most popular and widely-present choice in enterprise. The ecosystem is absolutely huge and there are tons of things you can add to make it support everything you need.
One of the most criticised aspect of using Redux with React for a long time was its verbosity, and let’s agree it was definitely the case until not long ago. But since the addition of redux hooks (which make Redux usable inside Functional Components), it feels like it’s lost a lot of its complexity.
Not as popular as Redux and has been the underdog since it came out. Mobx chooses a different approach that some people tend to prefer (with multiple stores and mutable states).
A simple abstraction layer above React Context. If you don’t want to directly use the low level Context API but don’t actually need the level of abstraction that Redux & Mobx offer, this is definitely a good option for up to medium-sized applications.
Graphql with the Apollo client cache
When Graphql came out it took the world of Web Development by storm. Working with it on a full Graphql stack feels really smooth, even though the entry cost may seem a bit high if you’ve been used to working with a more classical approach based on REST Apis for a long time.
Fortunately, tools like Hasura (https://hasura.io/) allow the use of Graphql with existing backends without having to rethink your entire architecture. And the Apollo client on the frontend side offers a neat way of handling your application state.
Working with Graphql is definitely something we have considered and something we would recommend to anyone starting a brand new product on a brand new stack.
But because we mostly focused on providing our teams with something they can plug-and-play to start new frontend applications using existing APIs, we decided to stick to a more traditional approach for this stack. That being said, if you have the occasion and haven’t already, definitely try it out!
Decision:
The way Redux managed to incorporate hooks inside their API made using the library much less of pain. Coupled with the very strong community around it and all the existing middlewares (we will be using redux-saga to handle any async actions), we firmly believe Redux is still a very strong choice for an enterprise-sized application in 2020. This article (even though it is from almost 2 years ago) highlights some of the points that helped us take that decision: https://blog.isquaredsoftware.com/2018/03/redux-not-dead-yet/.
UI Library
🥇 Our choice: Ant Design
Searching for React UI libraries quickly reveals what seem to be the 3 leading actors in that field: Material-UI, React Boostrap and Ant Design. They all have a strong community behind them, offer a tons of components, with various levels of customisation. So how do we pick one? To us the two most important aspects are:
- Do we like the overall look and feel of the library
- Most importantly, how easy is it to customise everything: the theme, the components, override the default behaviours, etc.
For the first reason we decided to eliminate React Bootstrap. That’s awfully subjective, yes, but after the massive Bootstrap wave in the early 10’s it just doesn’t really look “fresh” anymore.
It left us with Material-UI and Ant Design. Having used Angular Material for years on various previous projects, we were already pretty familiar with its API and UI. And we see some downsides using it:
- Similar to Bootstrap, it still gives a very “Googlish” looking aspect to your application. This might not be an issue for most people, but we would like something more neutral going forward.
- Deep customisation is hard. Material components come with a lot of stuff around them and, as with Angular, it works well until you need to modify something that was not meant to be modified
- It’s BIG! And we’re not talking about the bundle size, but the actual components. They all look massive, and it’s often pretty hard to tweak the core CSS to make them not take half of your screen each time (yes we’re looking at you, Outline Text Input).
On the opposite side, Ant Design comes with a very neutral UI and seems to offer more flexibility, as well as more components and a more in-depth documentation.
And what about the smaller players? It might be a bit in contradiction with what we said above, but one library caught our eye. Chakra UI. It made a pretty good impression when it was released last year, and after reading their documentation we were intrigued enough to include it into our test.
Decision:
Eventually, we decided to go with Ant Design for several reasons:
- Their list of components is ridiculously massive. As a B2B company we don’t really have a strong need for a custom design system (like Storybook) so the more components we can get out of a single lib, the better. Ant Design nails this point, where Chakra UI came a bit shy (not really a lot of “complex” components out of the box)
- It’s easy to customise. Even though using a custom theme required a bit of hacking around the webpack config, using Styled Components to extend the style of Ant components was really smooth
- The overall look-and-feel is really nice. It’s all about tastes and colours here, but as we said we wanted to get a bit away from the “Google look” of the Material lib.
- While not always perfect, the documentation provides us with a ton of different examples and code samples
We will definitely keep an eye on Chakra UI, maybe for smaller apps, but Ant Design is the more mature option here.
Styling
- TailwindCSS
- Styled Components
- JSS
- Plain CSS with a preprocessor (Sass / Less)
🥇 Our choice: TailwindCSS & Styled Components
TailwindCSS for the layout and general look-and-feel, Styled Component to customise Ant components
Now comes the styling of your app. There are just so many options! Plain CSS with a preprocessor, JSS, CSS modules, Styled Components, utility-first libraries,… each of them comes with valid pros and cons. So again, some choices here might seem a bit subjective, but more often than not in CSS discussions, it all comes down to personal preference.
Sarah Dayan gave a really good talk at DotCSS 2019 in the favor of utility-first CSS. We recommend watching it because we think it pinpoints some of the real problems we end up encountering with a more ‘classical’, component-first approach.
And one library in particular really caught our attention: TailwindCSS. There seems to be a real consensus around its capabilities, and the 1.2 version that just came out fills a lot of the missing holes from the earlier versions. So we decided to give it a more in-depth try!
But we still needed something to compare it against. Using SCSS or Less has done the job for years, but CSS-in-JS is definitely gaining more and more ground as time goes by. And for good reasons; it gives a more flexible approach to styling your app. Styled Components seems to be the easiest to pick up, and works perfectly with every UI library seen above.
The only experience we had with JSS was pretty bad, maybe we missed something but it felt a bit too cluttered and ‘low-level’. Material-UI is actually considering migrating from JSS to Styled Components in all their demos (pinned issues on their github).
Decision:
After working with it for 2 days, everyone agreed that TailwindCSS is really cool. There is a tiny bit of an entry cost if you’re used to write plain CSS but the productivity boost you gain from it is well worth it. Obviously, we cannot use it to customise Ant components so, as said above, we decided to also use Styled Components for that purpose. To sum it up:
- Everything related to the layout and general look & feel of the application: TailwindCSS
- Customising Ant components: wrap it inside a Styled Component
Forms
🥇 Our choice: React hook form
Formik with the help of yup seems to be doing a great job at taking the pain away from writing forms in React. More recently, react-hook-form joined the party with maybe a more modern API based on hooks. At the end we preferred the approach of the latter, as it just feels cleaner with hooks.
i18n
🥇 Our choice: LinguiJS
We are looking here to find an easy and flexible way of supporting a lot of languages inside our applications (the main product of Sidetrade supports more than 10 languages). react-intl and react-i18next seemed at first to be the most commonly used i18n libraries for React, so without looking too much into it at that time, we decided to evaluate these two.
Turned out none of the two groups managed to easily set up a proper i18n workflow (with extraction scripts and automated generation of translations files) with any of them! Trying to setup a semi-automated workflow where we can easily go from labelling a string in a component to a readable xml
or po
file did not seem very intuitive and felt pretty hacky.
Fortunately, we found a third, less known, library that seemed to fit perfectly our needs: LinguiJS. It comes with a really cool cli and automation scripts that are really easy to use. And it also supports number and date formatters out of the box! So, kudos to them for the good work.
Date Manipulation
🥇 Our choice: Neither 🤷♂
Moment is very large and doesn’t support tree shaking. There are a lot of libraries that offer the same level of features but without the heaviness. We narrowed it down to dayjs & date-fns.
But as said above, LinguiJs integrates a basic formatter for dates. And that will probably cover 90% of our cases. For the rest, to be honest we couldn’t find enough arguments to justify picking dayjs over date-fns or the other way around. So just for this one, we didn’t add anything to the list of dependencies for now and we leave it to the developers to choose which one they prefer if they need more advanced date formatting.
Bonus: VSCode docker container
Something really cool we found out while doing some research. From the official documentation, this extension allows you to:
use a Docker container as a full-featured development environment. It allows you to open any folder inside (or mounted into) a container and take advantage of Visual Studio Code’s full feature set. A devcontainer.json file in your project tells VS Code how to access (or create) a development container with a well-defined tool and runtime stack. This container can be used to run an application or to sandbox tools, libraries, or runtimes needed for working with a codebase.
So the idea is to version the configuration file (which includes VSCode extensions, the default command to be run, the app settings and much more) inside your application, and anyone who clones your repo can instantly start coding with the correct setup without having to worry about external dependencies or extensions to install. Pretty neat!
Conclusion
When we began this, we thought that plenty of teams would have already done this exercise and written about it. Weirdly enough, it turned out not to be as true as we thought! That’s why we’re happy to share here how this fun experience turned out, and the end results, accessible as a Github repo on our OSS page:
We will try to keep it as up to date as possible with the latest versions. Feel free to fork it for your own applications and comment below what you think of it — there is probably some interesting stuff we missed or overlooked.
We’re pretty sure not everyone will agree 100% with our choices! But at the end of the day, we picked the solutions that fit our needs and the ones we had the most fun with. Because it’s hard to write good code if you’re not having fun writing it 🙂 Happy coding!