Speed up your create-react-app build times by 80% with esbuild

Speed up your create-react-app build times by 80% with esbuild

let's give our good ol' CRA a little chance, shall we?

ยท

4 min read

A short view back to the past

create-react-app in 2017 (its v1 release) was this cool kid in around the block. We finally had a go-to way to start a react project that you couldn't go wrong with.

But as happens with everything on the web, people realized these client-side apps did not give out the performance they thought they would. Hence, came frameworks like Next.js. People/Organizations who could afford to move their react projects to these frameworks did that. While others stuck with CRA, with the hopes that things would get better.

Then came a time when people started experimenting with low-level languages (like Rust, Go for example) to replace JavaScript for our build tools and Hence things like Vite and esbuild were born.

We started seeing these massive differences in build times. Fact that Vite looked so similar but "Faster", everyone thought of just moving to Vite from CRA. But again there were these external packages at time that did not have an esmodule support and again people with am CRA app was stuck.

But then people like Hiroki came to save the day! They looked at the default CRA webpack config and said, "What if we replace the parts of this config, which are really slow with the equivalent APIs of esbuild?" and they did exactly that by building esbuild-loader

I did something similar to what we are going to do next at my previous workplace and let's just say the improvements were pretty wild ๐Ÿ‘€

Enough of the history lesson, give me the code already

We are going to use something like Craco, So that we don't even have to eject our CRA app.

Imagine using Craco, similar to sneaking up into the webpack config of CRA, changing the default webpack config just a little bit, without touching anything else ๐Ÿ˜‰

  1. Make sure your react-scripts package is updated to at least version 5.0.0 inside your package.json

  2. Install @craco/craco and craco-esbuild as dev dependencies

    We are using craco-esbuild here as it makes esbuild-loader a craco plugin, which makes the whole process a LOT smoother.

npm install --save-dev craco-esbuild @craco/craco
  1. Create a craco.config.js file in the root directory and insert this code snippet
const CracoEsbuildPlugin = require("craco-esbuild");
const webpack = require("webpack");

module.exports = {
  plugins: [
    {
      plugin: CracoEsbuildPlugin,
      options: {
        esbuildMinimizerOptions: {
          target: "es2015",
          css: true, //  OptimizeCssAssetsWebpackPlugin being replaced by esbuild.
        },
      },
    },
  ],
  webpack: {
    plugins: {
      add: [
        new webpack.DefinePlugin({
          process: { env: {}, browser: {} },
        }),
      ],
    },
    configure: {
      resolve: {
        fallback: {
          fs: false,
          tls: false,
          net: false,
          path: false,
          zlib: false,
          http: false,
          https: false,
          stream: false,
          crypto: false,
          buffer: false,
        },
      },
    },
  },
};

The first part of the config is just us adding a craco plugin called CracoEsbuildPlugin that taps into our webpack config and replaces babel-loader, terser and OptimizeCssAssetsWebpackPlugin. All of these plugins are being used under the hood of create-react-app when running the dev server and during the build process. We are replacing all of these with esbuild, that's part of the reason why we see these crazy improvements in the build times.

---------------
 configure: {
      resolve: {
        fallback: {
          fs: false,
...............................

Here we are telling webpack to not include polyfills for these node.js modules. Not setting them false explicitly, gave out some pretty bad compatibility errors with external libraries. You can read more about this part of the config here.

  1. Replace react-scripts build command with craco in package.json
+ "build": "craco build",
- "build": "react-scripts build",
+ "start": "craco start",
- "start": "react-scripts start",

That's it! Benchmark Time!

aaaand we are done! You know what they say, optimizing without any benchmarking isn't optimizing :)

Would LOVE to see the improvements if ya'll get any! There's a whole github discussion about the gains people make with esbuild.

If you made it this far, thank you for reading!

Do you think something's not right with the blog? is some part confusing you?

Please Reach out to me on Twitter, I am Tanvesh Sarve (@Sarve___tanvesh) / Twitter