Build a React JS application with ESbuild and node.
Recently we made a change in one of the projects for our client, We were tasked with changing the build loader of a ReactJS application from webpack to esbuild.
We didn’t worked before with any other loader apart from the default from react Webpack., so this was a challenge for us. Fortunately we had the help of Kevin Burke https://www.linkedin.com/in/ekrub/ to guide us and solve this daunting task.
With thee change from Webpack to esbuild we could shave 24 seconds of the app build 😆
We now have faster deployments and this is great!!
Esbuild -> Done in 481ms
Webpack build -> Done in 25.06s.
Based on Kevin’s work, we can change the loader from Webpack to esbuild with these easy steps.
- Create a “build” folder at the root of your project
cd your_project
mkdir build
- Create an index.js file as the holder for the rebuild file in the “build” folder
touch build/index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="icon" href="favicon.png"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="theme-color" content="#000000"/>
<link rel="manifest" href="/manifest.json"/>
<title>Dashboard</title>
<link href="/static/css/index.css" rel="stylesheet">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="/static/js/index.js"></script>
</body>
</html>
-
Add ‘esbuild’ package to you node_packages
yarn add esbuild
ornpm install esbuild —save
-
Add ‘regenerator-runtime’ to your app. This is need for making fetch calls.
yarn add regenerator-runtime
ornpm install regenerator-runtime —save
In your src/index.jsx
, add at the top:
...
import "regenerator-runtime/runtime";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>
)
...
- Create a build.js file on the root of your project
touch build.js
const { build } = require('esbuild');
const fs = require('fs-extra');
const generateBuild = async () => {
await fs.rmdirSync('./build/static', { recursive: true });
await build({
entryPoints: ['./src/index.jsx'],
outdir: './build/static/js',
minify: true,
bundle: true,
sourcemap: true,
target: ['chrome58', 'firefox57', 'edge16'],
loader: { ".svg": "dataurl", ".png": "dataurl" },
define: {
'process.env.NODE_ENV': "'production'",
...your env variables...,
}
}).catch(() => process.exit(1));
await fs.move('./build/static/js/index.css', './build/static/css/index.css', (err) => {
if (err) return console.error(err);
console.log("success!");
return null;
}
);
};
generateBuild();
-
Add ‘fs-extra’ package to your packages
yarn add fs-extra
ornom install fs-extra —save
-
Run your build
node build.js
-
Generated files should be in the ’build’ folder now.
-
You should change your build scripts to use this build command.
"scripts": {
...
"esbuild": "node ./build.js"
...
},
Extra Steps:
To use esbuild in development mode and watch for the changes in files:
As we are serving the build from a different folder and with different configurations we need to start the server on this new folder and using new configurations.
The react-script start the app by default in the current folder and looks by default for the src/index.jsx file unfortunately we can’t change this default behaviour without ejecting. So we are going to use a separate server (server) to start from our new build folder.
In the terminal run npx servor ./build/ index.html --reload
It will ask for install the servor
package
Then it will run with the options passed, and serve from the ‘build’ folder.
Now that we have the server running we need a way to watch for file changes and refresh the page. Chokidar package is great for this.
Lets install chokidar package
yarn add chokidar -D
or npm install chokidar --save
Modify the build.js script to add chokidar watcher
const { build } = require('esbuild');
const chokidar = require('chokidar');
const fs = require('fs-extra');
const generateBuild = async () => {
await fs.rmdirSync('./build/static', { recursive: true });
await build({
entryPoints: ['./src/index.jsx'],
outdir: './build/static/js',
minify: true,
bundle: true,
sourcemap: true,
target: ['chrome58', 'firefox57', 'edge16'],
loader: { ".svg": "dataurl", ".png": "dataurl" },
define: {
'process.env.NODE_ENV': "'production'",
}
}).catch(() => process.exit(1));
await fs.move('./build/static/js/index.css', './build/static/css/index.css', (err) => {
if (err) return console.error(err);
console.log("success!");
return null;
}
);
};
chokidar.watch('.', {ignored:/build|node_modules|.git/}).on('all', (event, path) => {
console.log(event, path);
generateBuild();
});
Don’t forget
In one terminal you should have the server running
npx servor ./build/ index.html --reload
And In another terminal you should have the build script running
node build.js
And that’s all. Enjoy your extra time!
Big thanks to Kevin for helping us decipher this.
You can find the code for this tutorial is in our GitLab account https://gitlab.com/alamedadev/react-js-build-using-esbuild