loading...

My React stack for 2019

pajasevi profile image Pavel Ševčík ・5 min read

Since several people recently asked me to share my ideal React stack, I decided to write it down and share it also with you.

Basics

First things first, we developers don't need to be distracted by simple problems and debugging of false positives. That's why I always use Typescript as it is a way to stay sane while writing an app.

Second, we want to have deterministic and easy to use dependency management - this is why I use Yarn. Not only it is super fast, it also has great features like yarn upgrade-interactive which lets you choose packages you want to upgrade (hint: also use optional flag --latest).

Routing: React Router

This is almost a standard for React apps nowadays but I still have to mention it. React Router is great library which provides a simple way to declaratively structure your app and handle static and dynamic routing.

It is also easy to integrate its Link components with other component libraries like Reactstrap or Material UI.

Styling: JSS

There are many concepts and libraries to handle styling in a React app: inline styling, linked CSS file, CSS modules, Styled Components, Emotion, etc. I spent almost three days researching which of these libraries would best suit my needs and finally, the answer was: JSS.

What I most love about JSS is its versatility and ease with which I can write and use styles. That's probably why Material UI chose to use it too.
Styles can either be compiled in the browser or in Node.js at build time. These styles are not tied to a particular component, but are just classes which makes them really reusable. JSS also comes with theming support and many plugins to make styling more convenient.

And for those using Typescript: your experience with styling will be a pleasure with JSS.

Honorable mention #1: clsx

To concatenate classNames, use clsx - it is faster and smaller than classNames library.

Internationalization: react-i18next

When it comes to i18n in React apps, it mainly boils down to two main competitors: react-intl and react-i18next. I chose the latter for a variety of reasons.

First of all: react-i18next has great documentation and is very simple to use. It has support for namespacing so your translations can be separated for better maintenance. It has support for lazy-loading which means your app can download only translations it needs right now (and you can deploy translation independently of your app).
And last but not least: the team makes a huge effort to stay up-to-date with latest React features which is why it already has React hooks support.

DateTime manipulation: Luxon

Ah, the accursed DateTime manipulation. I always preferred moment.js over date-fns but now I have new favorite which is luxon, made by one of the people behind moment.js.

While moment had some issues in its design it was good decision to make a new library for a couple of reasons.

For me, the main benefits of Luxon are:

  • immutability and thus predictability
  • build-in i18n support
  • great documentation => easy to use

It also comes with support for Duration and Intervals.

Honorable mention #2: Lodash

When talking about utility libraries, we can't forget about Lodash. This library makes it very easy to work with data structures, Objects and Arrays in the sane manner.

API communication: Axios, Superagent

The world has been much nicer since the invention of Fetch API. We no longer need to make AJAX calls with jQuery. But still - who wants to remember how to create a JSON POST request with native Fetch? And who wants to write their own API wrapper? That's so 2014...

This is why there are libraries like Axios or Superagent that make API communication easier for us. They're both universal (work in browser and Node.js), they both use Promises because they're based on Fetch API and they're convenient and easy to use even for more robust use-cases.

State management: MobX

Don't get me wrong - Redux is great for storing data in large-scale apps where global state is a must. Its predictability makes it perfect for managing all possible states the app can have and the ability to replay these states is incredible. That said, writing so much of the boilerplate code for state management can be a bit exhausting (and an overkill) for small/medium-sized app.

This is where MobX comes in handy. It is easy to use, scalable state management library that does all the hard work for you. It is based on the Observer pattern which basically means that when you change a state value via MobX action (which is just a decorated function), your connected components automatically update.

That's what I call easy peasy.

Form handling and validation: Formik & Yup

Forms and how to handle them properly. What a big topic of React discussions.

For me, Formik found the perfect balance between being "the tool that does everything for you" and "the library that doesn't strap your hands".
It's really versatile - it handles form state, validation, errors, loading and submission and lets you decide if you use its components or write your own.

Thankfully, Formik didn't try to come up with its own validation engine and you can choose whatever you want. On the other hand, it comes with support for Yup validation library which is very convenient.

Yup is an object schema validator which provides validation functions for (almost) all javascript types and ability to create custom validators. It is also very easy to integrate with i18n libraries, thus you can (and should) define your own validation messages. You are also able to define conditional validations, so some values are validated differently based on other values.

Online tools

I also decided to share a couple of online tools I use to search for and work with these libraries:

  • npms.io - fast and reliable search engine for node modules which includes great algorithm to score libraries
  • DevDocs - imagine all libraries would have documentation in one place, perfectly searchable, available offline. Well that's what DevDocs it trying to achieve and is doing great.
  • TypeSearch - for anyone who wants to use Typescript, this is a must. Search for available Typescript definitions for existing libraries.

I hope you enjoyed reading my list of tools. Do you have any favorite library which you can recommend? Leave a comment and let's talk.

Discussion

markdown guide
 

Nice share!

Second, we want to have deterministic and easy to use dependency management - this is why I use Yarn

I'm curious, why are you using Yarn in 2019? npm offers much of what Yarn does - and more. Plus it comes out of the box with every node installation. The fewer tools, the better!

The world has been much nicer since the invention of Fetch API. We no longer need to make AJAX calls with jQuery. But still - who wants to remember how to create a JSON POST request with native Fetch? And who wants to write their own API wrapper?

Writing a wrapper around fetch to support the basic use cases takes ~50 LOCs and saves you a couple of KBs from Axios (which, by the way, uses XMLHttpRequests under the hood!). Plus easily allows you to hook into the configurations when you need to do stuff like auth token, so I'm curious if you're taking advantage of features such as Axios interceptors or it's very wide browser support. Otherwise, seems like there's not too much value on using it.

I'd imagine that you still abstract away data fetching from your React Components using Mobx async actions (not doing so would be a crime on middle-to-large projects).

 

afaik fetch

  1. does not have a way to track progress
  2. cancelling can be cumbersome(abortController is not supported by IE and claimed to be experimental)
  3. there a must be wrapper code to translate 4xx/5xx status codes into rejected promise.

to me looks more than 50 loc to write.
or has thins been changed recently?

 

Hi, thanks for your input

npm offers much of what Yarn does

I disagree:

  • Yarn is much faster
  • Yarn workspaces allow for easy monorepo management
  • Yarn has upgrade-interactive. NPM has a separate package for that.

Writing a wrapper around fetch to support the basic use cases takes ~50 LOCs and saves you a couple of KBs from Axios

You're right. But writing those 50 LOC for every project may seem like a waste of time (of course there are still cases when it is better choice). I could make a module from it, but why would I create another Axios? I never needed to use interceptors, I'm using Axios because the API is nice and it offers pretty features out of the box.

I'd imagine that you still abstract away data fetching from your React Components using Mobx async actions

Yes, of course :-)

 

Yarn is much faster
Yarn workspaces allow for easy monorepo management
Yarn has upgrade-interactive. NPM has a separate package for that.

Can you link me to the benchmarks? Much of the posts that I find says that they're par on speed. Just now I tried both a cold install and a warm install inside a Docker container and got these results:

npm cold install: 48s
yarn cold install: 1m12s

npm warm install: 24s
yarn warm install: 20s

Note that for CI environments, npm has npm ci which install the dependencies 3 times faster. I didn't find a yarn equivalent.

About monorepos and upgrade-interactive: I don't use monorepos and I update dependencies like once in a month, so I don't actually mind those features. Like you said there's a separate package for both features so you can do:

npx npm-check --upgrade
npx lerna ...

There's no npx equivalent on Yarn.

Some recent benchmarks are here. They're on par most of the time, but Yarn is pretty fast with cache and lockfile.

Easy to be fast with cache when Yarn cache litterally everything (every month I needed to remove 80GB of data from Yarn cache when i was using it)

By the way npm ci is what you use normally when you're not locally developing on the package so every installation aside your local install is 3x faster.

For mono repo, there's lerna which you can use via npx

 

Have you tried Ramda over Lodash?
curry everything is so awesome, and everything is compossible

Lodash

const myFn = (input)=>{
  const multiply = _.multiply(input, 2)
  const add = _.add(multiply, 1)
  const add = _.add(multiply, 1)
  return add < 0 ? add * -1 : add
}

myFn(-4) //=> 7

Ramda

const myFn = R.compose(Math.abs, R.add(1), R.multiply(2))

myFn(-4) //=> 7

Another Example
Lodash

const myFn = (list)=> _.map(list, (n) => n * 2)

myFn([4,8]) //=> [8, 32]

Ramda

const myFn = R.map(R.multiply(2))

myFn([4,8]) //=> [8, 32]
 

I was recently looking into more functional paradigm and Ramda seems great. Hopefully I will have a chance to use it soon.

 

I’d also recommend trying rxjs! It’s simply awesome!

 

When deciding on which npm package to include, I always get a little paranoid over possibly making my bundle size too large. Just last week I found this amazing tool bundlephobia.com that shows you the size and dependency makeup of [almost] any npm package.

 

Awesome! Thanks for sharing!

 

I'm not fond of React Router
I pretty much prefer using a simple router which plug on my redux store. That lets me have one single source of truth. When not using redux why not use React Router

For redux the boilerplate you talk about is lesser than you think, everyone which use redux want to reason with (reducer, selector, actions, actions creator), you don't need all this even with redux.

By the way I do think data fetching can be achieved by using Suspense and relatively well thought cache

 

I don't think that linking Redux with router is a good practice. I think it is much closer to presentational data and presentational data belongs to component state. That's why I would never put form state to Redux state.

 

What lies in redux store is widely use data, it could be some application state (login state, sidebar visibility state, current route - routing)

What lies in component state is the state of this said component.

If i'm extrapolating your point, if I have flag which tells me if the sidebar is open (pure presentational data) I have to store it in the state of the top level component and pass the value or/and the update function for this value for all the components that need it ?

Basically yes. It's a separation of concerns - presentational data are not mixed with app data. Altough it could also be achieved using separate redux store.
But this is just my opinion, not a definitive answer to app state management. Clearly there is a lot of ways and what's important is that everything stays clear and easy to maintain/develop/debug.

 

That's what I call easy peasy.

FYI I have a state management lib under that name 😛

 

That's a great coincidence! 😆
Anyway, it looks really good, I might give it a try on some side project.
Thanks!

 

Terrific post! Took note of comparison between i18next and intl.
Perplexed with Luxon vs Moment 2.0, anyone could share an opinion?

 

Pretty good stuff, I also plan to do some tutorials posts about React

 

Thanks a lot, sir. for nice and informative post. I hope to learn and apply them to my apps. Once again, Thank you so much.

 

Thanks for sharing. I'm also working at Globant. I've said the post from the development channel.