I’ve been using React for more than a year now and a lot of the time things broke, slowed down our build speeds, and didn’t work in the browsers I was transpiling for. I decided to share the lessons I’ve learned in the hopes they might help others out.

Be very careful with importing large libraries

Trying to compile large libraries (like react-plotly or PDF libraries) can take your Webpack compile from seconds to 10 minutes+. If a package is slowing down your compile, consider using a CDN version. We simply used script tags, but there are Webpack plugins that can help with that too:

Try to find a webpack plugin for your dependencies

Just importing packages like moment.js or lodash brings in a lot of bloat that you probably don’t need. Try to import what you need only, or better yet find a webpack plugin that removes the unused things from your bundle, because selective imports don’t always work. As one example, there’s a webpack plugin that removes a lot of the unnecessary bloat added by Moment.js.

Google actually has a nice repository listing some common problematic dependencies.

Inspect your bundle with Webpack bundle analyzer

moment.js files inside a bundle

Webpack Bundle Analyzer is extremely helpful to see what exactly is going into your bundle. In the screenshot above, you’ll notice that moment.js has lots of localization files that your app probably doesn’t need. Webpack Bundle Analyzer can help you easily spot these issues.

Add es-check to your CI pipeline early on

es-check will help you find out which ES version your bundle is using, it’s super useful to find out if you’re somehow suddenly not producing ES5 anymore. Even if you’re using Babel and browserslist, you might be importing a node module that’s not even meant to be used in browsers, or even a package that’s not being distributed as ES5. Add es-check to your continuous integration pipeline early on and it should help you find out if your bundle ever stops working with ES5, and that’ll help you find which package is the culprit so you can then transpile it.

Transpiling a node_module

We had imported a very simple package called hex-rgb that’s not even meant for browsers and this tiny package made our bundle not ES5-compatible anymore. Such packages should go through Babel and be transpiled.

In your webpack config, your babel loader’s exclude field probably looks like this: /node_modules/ . We need to make a regex that excludes node_modules except the specific ones that should be transpiled:

// Exclude all node modules except hex-rgb and another-package
/node_modules\/(?![hex\-rgb|another\-package])/

And once again, this might not be a good solution for large packages as it can drastically slow your build time and you might want to switch to a CDN version instead.

Follow this issue from the babel-loader repo to stay up to date on how to handle cases like this.

Use Browserslist to specify your target browsers

Browserslist lets you specify which browsers to transpile for.

> 1%
ie >= 8

This simple configuration targets browsers with usage more than 1% global usage, and IE versions 8 and above.

Use babel.config.js over .babelrc (for Babel ≥ 7.0)

Favor using babel.config.js to configure Babel over .babelrc . If you want to transpile node_modules (which is now becoming a very common case with webapps), then you should use babel.config.js .

.babelrc can be overridden by another .babelrc belonging to a node_module that you’re transpiling and that can lead to all sorts of weird issues.

Make your webpack-dev-server logging output friendlier

  1. Change your webpack-dev-server config to this
devServer: {
  noInfo: true,
  stats: 'minimal'
}

2. Add WebpackBar to get much less-verbose, friendlier, and more concise output.

Note: The first configuration is meant to be combined with Webpack Bundle Analyzer, as it suppresses console output for things related to your bundle that Webpack Bundle Analyzer already shows. If you're not using Webpack Bundle Analyzer, don't apply the first step.


What I hope to see

We need a lot of improvements in our tooling around this process. Most webpack and Babel configs exclude the node_modules folder (with good reason, compilation would take an insanely long time if they didn’t!), but I personally feel this is an area where we need smarter tools. It would be quite convenient if everything “just worked” only by configuring Webpack, and Babel along with browserslist without having to dig through dependencies and the often unique problems they introduce to our build pipeline ourselves. The package.json file could be a good starting point towards this goal. There are a couple of things that if used could help tools determine if a package should be tranpiled or not:

  • The browser field, this should be used instead of the main field if your package targets browsers. Meaning that we should at least get a warning if we specify that we target browsers in our package.json and we import a package that doesn’t have the browser field in its package.json.
  • The engines field, This is usually used to specify the node versions your package targets, but I honestly think it should also be used to specify ES versions, since a lot of NPM’s usage is targeting browsers nowadays.
  • If a package doesn’t have any of these fields and we specified that we target browsers and ES5 then we should:
  1. Get a warning when we import it.
  2. Have Webpack plugins that detect these packages and un-exclude them so they get transpiled automatically.

Closing Notes

Webpack and Babel are awesome, this article isn’t trying to say otherwise. We wouldn’t be able to use a lot of things if it weren’t for them, but the experience of using them together needs to get better. I faced many of these issues over the course of many months and understanding them/finding solutions was incredibly difficult (error messages don’t exactly tell you what went wrong and searching for them doesn’t always give relevant results), and I hope this article can act as the guide I had hoped to find back then.