Solutions for React app development

Kirill Konshin
Mar 2, 2017 · 11 min read

Whenever you start a new project you either end up choosing one of the options:

  • copy pasting one of your previous projects;
  • use some boilerplate or even a boilerplate generator (like Yeoman);
  • use config-free solution;
  • build all from scratch.

Each way always have it’s own pros and cons, both short term and longer term.

Solutions can hide initial complexity in the beginning and provide a quick start, basically, a turn-key solution, but can be very painful whenever you customize them. On the other hand, they can be a bit bulky and in order to start you must do some heavy-lifting, but they will serve you well over time. Also there is custom, where only you define how things will be arranged, but you need to care about tons of aspects all by yourself, which require a very broad knowledge of all involved technologies.

Having company-wide standard approach provides a lot of benefits for big company where several UI projects (internal and customer facing) can coexist, so that any team can maintain any project. Or for design studio that makes websites for clients, where setup cost of each new project should be as low as possible. Unified approach allows to transfer knowledge between teams and team members, educate newcomers quicker, have a shared knowledge base of technologies, best practices, known issues and more.

Analysis of the market shows that there are few established solutions that can assist with making new React apps. We specifically selected solutions with different nature in order to show all the pros and cons of each approach.

Candidates

Custom solution parts

Aspects of modern app-making process

I wanted to make this list as full as possible, outlining all possible cases. Some of them have conflicts with others.

Cacheable chunks

If you build your entire app into one JS file, then initial load will become too heavy, users will observe a loading page for quite long period of time. Studies show that users tend to leave the website if it loads for more than 3 seconds.

  • Next.js automatically creates a chunk for each page
  • Electrode, Create React App and custom can use React Router. Webpack, in turn, has code-splitting which plays well with React Router. FYI, dynamic import() statements are now supported by Webpack 2 natively. For slowpokes who still have Webpack 1.x there’s a solution too. Electrode has a special archetype for that.

Static files management

App should deliver static up-front to reduce the amount of roundtrips (styles, scripts, images), e.g. commons chunk with all vendor stuff, important CSS and so on. But at the same time it should deliver only what is needed for particular page.

  • Create React App does not allow to modify Webpack config, so you’re a bit locked here
  • Next.js, Electrode and custom has ability to customize Webpack config and manage chunk contents via Webpack plugins

Server Side Rendering

Pre-bake important data or even initial markup into the initial HTML so that when user opens the website there will be almost no delay between the request and the time when site is at least visible, not talking about “useable”, that’s a different story.

Google ranges websites based on response time too, also despite the fact that it can render an AJAX website, it will badly affect it’s position, so Search Engine friendly pages is a must for some cases.

There is one issue with any of the approaches: the entry point has to load not just own data, but also data of all involved widgets, and seems that there is no established way to automate this. In custom we kinda can traverse the React tree and use some static method like getInitialProps of all involved components.

  • No such thing in Create React App, not even in roadmap
  • Next.js and Electrode have it by default as a core feature
  • For custom you have to build your own setup
  • There is another way of optimization: Relay + GraphQL, but after digging it for quite some time I figured that it is very verbose (in GraphQL definition and in client-side mutations especially). The complexity of code that you need to write by hand in order to make a simple ToDo list is so high that it practically ruins all benefits, just look at those mutations, it’s just to save a ToDo. It will be super hard to explain to anyone else later. Also it requires a custom Babel transform, schema build process, etc. So I ruled out this approach. Also it’s not SEO-friendly because initial HTML is pristine. But I have to admit that if you consume some GraphQL API and you don’t have to write one yourself, then take a closer look, it is a very promising technology, I hope guys will find a way to make it more friendly. I should also mention Apollo Framework here.

During this research I have made few open source libraries aimed to help with Server Side Rendering:

Performance of Server Side Rendering

Caching of rendered pages at some point becomes crucial under heavy load.

  • Electrode has a plugin for optimization, for production it compiles all you have into plain old JS and uses this for SSR
  • Next.js does not provide anything yet, it also caches the compiled code
  • In custom you always can DIY, you will have to manually find a way to compile your server and client portions, save them somewhere and pick them up properly in proper places, Create React Server by default assumes that you compile your server in runtime (which is fine because it just affects the startup time and eats a bit more memory in the beginning)
  • Create React App does not have SSR

CSS preprocessors

Many people use LESS/SASS/Stylus/PostCSS to pre-process their stylesheets, this allows to use variables and mix-ins to keep the code cleaner.

  • Electrode and Next.js does not support them by default, but you can override it’s Webpack config, which is not recommended though
  • Create React App has no support for this, and they have an explanation why (TL;DR use composition)
  • For custom you can make it via Webpack loaders
  • For any of the candidates you always can set up a separate SASS/LESS builder/watcher (just like in Create React App example), but you will have to manually include your resulting CSS somewhere in your app. In this case Critical CSS and Hot Module Reloading and Server-Side rendering of styles becomes your responsibility, e.g. DIY thing, so you loose benefits of non-custom solutions

Third party UI lib integration

Obviously, it’s good to reuse best libraries in order to speed up the delivery of your product, especially if we talk about things like Twitter Bootstrap or Material Design, which will make your UI look more robust and clean. These libraries usually come with some CSS that you have to include in your pages.

  • In Next.js you can include libraries’ CSS directly in _document.js (your initial HTML markup), but then you need to put the actual file to static directory; if you use preprocessors then include from there (you should already have either custom _document and builder or additional loader in custom Webpack config)
  • Electrode provides support for CSS, you can directly include it in your JS; or add via custom HTML template (but then you also will have to add it to the build); or add custom Webpack loader
  • In Create React App just add LINK with stylesheet to HTML template

Critical CSS / Above The Fold CSS

This feature allows to embed some of the CSS into HTML so that users will never see FOUC. Good to have, but not so important since modern networks have good bandwidth and with GZip all CSS can be less than 100k.

  • Electrode has a special plugin for that
  • Next.js has CSS in JSX which is always embedded in component, so it’s always delivered along with JS
  • You can kinda DIY for others

Skin Support

Not required for everyone, but it may be required for branding or visual pleasure of users.

  • Next.js has CSS embedded in JS, so you can use JS approach and return CSS from a function, which takes variables as arguments
  • Electrode rely on CSS modules which does not have variables
  • Create React App does not support separate stylesheets because it extracts all CSS into one file
  • If you use CSS preprocessors and you have custom build/watch processes, then you can have your base styles stuffed with variables and have few style entry points with overrides (like in Twitter Bootstrap)

Ecosystem & Community

Live within the ecosystem and community, e.g. a framework, well-designed and well-documented by experienced nerds, is better than a ton of micro libraries, less dependency management, one vision, available solutions, etc., as said “if you don’t use a framework, you end up building one”.

  • Electrode has ~500 stars on Github, active members of community answer questions in issues and in StackOverflow. Documentation is messed up and bloated with huge and irrelevant examples, but you usually can find the answer.
  • Next.js has ~9000 stars on Github, super quick response to PRs and issues. Has some documentation covering beginner and advanced cases, has lots of example projects for popular setups and libraries.
  • Create React App has ~21.500 stars on Github, its’ the biggest community. It’s so small that there is virtually nothing to document, but they managed to provide lots of examples and recipes.
  • In custom you are on your own, your communities are scattered across all packages that you will use, you have no established ecosystem, and you are basically the only one with knowledge of your solution which is bad for the company. You will have to write your own documentation (which most of us will never do).

Tests

Nobody would argue that tests nowadays are a must have feature of any development lifecycle.

  • Electrode has PhantomJS based tests along with server side tests
  • Create React App comes with Jest which is good, but does not test in browser
  • Next.js and custom does not offer anything

Boilerplate, Configuration, Setup Costs

Since we plan to create many projects based on the same standard we better have as less boilerplate and configs. The initial setup should be easy and straightforward.

  • Next.js and Create React App have zero configuration, just put your stuff in the right place and you’re good to go
  • Electrode has a Yeoman generator which produces a huge pile of scripts, you have to learn what is where and get used to it, but the amount of boilerplate is huge, you got configs, server-side scripts, client-side scripts and it does not seem that you can get away from it.
  • In custom setup you will have some boilerplate anyway, but if you make dozens of projects you can abstract some repeated patterns and create your own templates/generators/packages that will speed up your initial setup. Frankly speaking you can do that for Electrode too.

Learning curve at first glance and for advanced cases, customization

Since most likely you will not be the only one developer who will work with your stuff you have to be prepared to mentor others. Documentation and well established patterns is a very good foundation for knowledge transfer inside the team and cross teams.

Ability to customize anything because it’s real life with weird and requirements, some of them may not be supported by frameworks.

  • Create React App is the simplest to understand, but when you start to put more and more libs there it may become more and more complicated, especially for advanced cases. Source code is a mess so if there is a possibility to “eject” (meaning extract all the build/lifecycle scripts & configs) you better start looking and other candidates, because there is practically no life and benefits of this approach after “eject”. Comes with linter.
  • Next.js takes care of *some* aspects, which requires some time to initially understand what’s going on, but majority of simple cases already have recipes so no confusion here. Unfortunately when you start to customize your app it quickly becomes messy and full of wobbly 3rd party solutions. Basically, any step out of original concept is a big pain. No linter.
  • Since Electrode is heavily based on configuration and majority of scripts are exposed anyway it’s hard to understand in the beginning, but once you get the concept, it’s somewhat easy to tune and bring advanced stuff in. Unfortunately, there’s a lot of magic involved too, so some cases could be painful. It also comes with a strong ESLint setup.
  • In custom you are on your own… nothing much to add. You have to find the best practices, structure your app according to them, make documentation, and so on and on.

Internationalization

Your localization string should be in one of standard formats, which can be understood at translation firm *and* should support pluralization, token substitutions, etc. because at the end of the day we program things for humans, no nerds or scientists.

  • None of frameworks provide anything special for this. So far the best way that I have figured is a centralized loader of strings which will load a certain lang pack on demand via import() and return it: const loadStrings(lang) => import('./strings/'+lang).
  • Libraries: FormatJS and Format Message (which is basically an ICU Format)
  • Organize your lang files in any way you want, but it appears that token based approach (e.g. {TOKEN: {en: 'English', fr: 'French'}} works the best for devs and translation teams (instead of english strings that work as tokens).

Comparison

+------------------+-----------+-----------+-----------+-----------+
| Name | React App | Electrode | Next.js | Custom |
+------------------+-----------+-----------+-----------+-----------+
| Dynamic routes | yes | yes | DIY | yes |
| | | | | |
| Server rendering | no | yes | yes | DIY |
| | | | | |
| SSR Optimization | no SSR | yes | no | DIY | | | | | | | | CSS | yes | yes | nightmare | DIY easy |
| | | | | |
| Preprocessors | nightmare | nightmare | nightmare | DIY easy |
| | | | | |
| Critical CSS | DIY | plugin | no | DIY | | | | | | |
| Community | big | yes | yes | all alone |
| | | | | |
| Tests | jest | phantom | DIY | DIY |
| | | | | |
| Boilerplate | zero | lots | zero | lots | | | | | | |
| Config | zero | lots | zero | lots | | | | | | |
| Documentation | good | ugly | okay | scattered |
| | | | | |
| Learning curve | flat | moderate | easy | nightmare |
| | | | | |
| Advanced learn | nightmare | steep | steep | nightmare | | | | | | |
| Server Engine | nginx | node | node | node |
| | | | | |
| Customizations | eject-die | so-so | nightmare | by design |
| | | | | |
| Setup cost | easy | verbose | easy | nightmare |
| | | | | |
| Predictability | good | so-so | bad | nightmare |
+------------------+-----------+-----------+-----------+-----------+

Verdict

If you don’t need Server Side Rendering (you don’t plan to sell anything or all your app is private and Google does not index it), then lean towards Create React App as it’s the most popular and most simple.

If you need SSR and you are OK to deal with certain limitations of frameworks then choose Electrode (you should also be OK with verbosity and massive configurations). It also is a good choice if you really care about performance.

If you can afford a bit more limitations than Electrode has and also you hate configs choose Next.js.

Otherwise go ahead and dive into the world of custom. Luckily with tools like Webpack Blocks, Create React Server, React Router, Redux and others you should be just fine. The only problem is how to transfer knowledge and how to establish a process allowing to create new apps frequently without pain and copy-paste.

D i S

Developer/ DJ / Producer / Photographer / Traveller

Kirill Konshin

Written by

Staff Software Developer @ RingCentral, Inc., Music Maker, Traveller, Photographer

D i S

D i S

Developer/ DJ / Producer / Photographer / Traveller