How to build code generated by create-react-app with esbuild

esbuild is js bundler that is getting more and more traction because of it impressive build speed. create-react-app (CRA) is well-established script to generate a new react application. In this article, we will take a look on what tweaks are needed to CRA output to pass build with esbuild.

Alternatives

There are some react app generators that use esbuild out of the box:

and those can be a good idea if you are starting a new project, but for already existing apps they probably will not be of much help.

Generting new application

To simplify our example, let's generate new application with CRA:

$ npx create-react-app esbuild-cra

Creating a new React app in /home/marcin/workspace/github/tmp/esbuild-cra.

Installing packages. This might take a couple of minutes.
...

We suggest that you begin by typing:

  cd esbuild-cra
  npm start

Happy hacking!

The next step is to install esbuild:

$ npm install --save-dev esbuild
added 1 package, and audited 1962 packages in 4s

146 packages are looking for funding
  run `npm fund` for details
...

Adding test HTML

For testing the esbuild we will create a dist folder, and put there manually created index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <title>React App</title>
    <link href="main.css" rel="stylesheet" />
  </head>
  <body>
    <div id="root"></div>
    <script src="main.js"></script>
  </body>
</html>

I created it based on html output of npm run script, and replace all dynamically created file references with static one that we will build in a moment.

Build script

To build the application, I'll leave the original npm scripts as they were & add temporarily esbuild script for testing. I recommend doing the same if you decide to migrate your app - it will allow for a smooth testing before replacing original builds. Plus you will want to replace dev script too - something that is not covered in this article, and you will need to figure it out on your onw.

To package.json, I add:

  "scripts": {
    ....
    "esbuild": "esbuild src/index.js --bundle --outfile=dist/main.js --loader:.html=text --loader:.js=jsx --loader:.svg=dataurl"
  }

The build script parts means as follow:

  • src/index.js - the entry point of our build. From there all our files are found
  • --bundle - the files are all bundled into one, output file
  • --outfile=dist/main.js - the location where the output files should be saved
  • --loader:.html=text - a loader that will read all HTML files are strings equal to the file content
  • --loader:.js=jsx - the JSX load for JS files - as CRA uses this extension for JSX files
  • --loader:.svg=dataurl - loader that includes SVG files as data url links (data:image/svg;base64....)

With this script in place we already can successfully run npm run esbuild:

$ npm run esbuild

> esbuild-cra@0.1.0 esbuild
> esbuild src/index.js --bundle --outfile=dist/main.js --loader:.html=text --loader:.js=jsx --loader:.svg=dataurl


  dist/main.js   902.9kb
  dist/main.css   1019b 

⚡ Done in 61ms

Add explicit import

If you open the test page as it is now, you will see a white screen and the following error in console:

Uncaught ReferenceError: React is not defined
    App http://localhost/github/tmp/esbuild-cra/dist/main.js:20611
    renderWithHooks http://localhost/github/tmp/esbuild-cra/dist/main.js:12714
    mountIndeterminateComponent http://localhost/github/tmp/esbuild-cra/dist/main.js:14834
...

luckily it's easy to fix, we just need to import React in all places that are referencing it. In our case, just add to src/App.js this line as the first line:

import React from "react";

Links

Summary

In this article, we have seen how to build esbuild an app generated with create-react-app. If you are interested in other articles on this topic, let me know in the comments.

You can see the application in action here, and the repository here