Lightning-Fast React Setup 2024: How to Build a React App with esbuild in Minutes

Introduction

In the fast-paced world of web development, efficiency and speed are crucial. React, one of the most popular JavaScript libraries for building user interfaces, has empowered developers to create dynamic and responsive web applications. However, as projects grow in complexity, the need for a robust and lightning-fast build process becomes increasingly important.

Enter esbuild, a modern JavaScript bundler and minifier known for its exceptional speed and simplicity. Unlike traditional bundlers like Webpack, esbuild is designed to handle large projects with ease, offering near-instant build times and minimal configuration. As we step into 2024, esbuild is gaining traction as a go-to tool for developers who want to streamline their React development process.

In this guide, I’ll walk you through the steps to set up a React project using esbuild. Whether you’re a seasoned developer looking to optimize your workflow or a beginner eager to learn, this guide will equip you with the knowledge to get your React app up and running in minutes. By the end, you’ll have a high-performance, production-ready React application built with the power of esbuild.

What is esbuild?

esbuild is a modern JavaScript bundler and minifier that has quickly become a favorite among developers due to its incredible speed and simplicity. Written in Go, esbuild is designed to be fast—blazingly fast. It can handle complex bundling tasks in a fraction of the time it takes for other tools like Webpack or Parcel.

One of the standout features of esbuild is its zero-config approach. Unlike traditional bundlers that often require extensive configuration, esbuild works out-of-the-box for most common use cases. This simplicity makes it an ideal choice for developers who want to get their projects up and running quickly without getting bogged down in configuration details.

Here are some key features that set esbuild apart:

  • Lightning-Fast Builds: esbuild is designed to perform bundling tasks at unprecedented speeds, making it ideal for large projects or frequent rebuilds during development.

  • Minimal Configuration: With sensible defaults and an intuitive API, esbuild allows you to start building with minimal setup.

  • Tree Shaking: esbuild automatically removes unused code from your final bundle, helping you deliver optimized and lightweight applications.

  • ESM and CommonJS Support: esbuild fully supports modern ES modules as well as the older CommonJS format, ensuring compatibility with a wide range of libraries and tools.

  • Integrated TypeScript Support: If your project uses TypeScript, esbuild can compile your TypeScript files directly, without the need for additional plugins or tools.

In 2024, as developers seek faster and more efficient ways to build web applications, esbuild is emerging as a game-changer. Whether you're working on a small personal project or a large enterprise application, esbuild offers the performance and simplicity needed to keep your development process smooth and efficient.

Setting Up Your Development Environment

Before you can start building your React application with esbuild, you’ll need to ensure that your development environment is properly set up. In this section, we’ll guide you through the essential steps to get everything ready, from installing the necessary tools to initializing your project.

  1. Prerequisites

    Node.js and npm/yarn: Node.js is a JavaScript runtime that allows you to run JavaScript code on your machine. npm (Node Package Manager) or yarn is used to manage packages and dependencies for your project.

  2. Code Editor

    While you can use any text editor, Visual Studio Code is highly recommended due to its powerful features and extensive ecosystem of extensions.

Creating a Basic React Project

Let's set up a basic React project:

  1. Installing Locally

    First, navigate to the directory where you want to create your project. Then, run the following command to create a new directory and navigate into it:

     mkdir esbuild-react-app
     cd esbuild-react-app
    
  2. Initialize a new Node.js project with npm:

     npm init -y
    
  3. Now, install all the necessary dependencies and devDependencies:

     npm i dotenv esbuild react react-dom typescript
    
     npm i -D @types/react @types/react-dom esbuild-css-modules-plugin esbuild-jest glob prettier shelljs
    
  4. Set Up the Project Structure:

    • Create the following folder structure:

        esbuild-react-app/
        ├── esbuild/
        |   └── dev.js
        |   └── prod.js
        ├── public/
        │   └── index.html
        |   └── manifest.json
        |   └── favicon.png
        ├── src/
        |   ├── contexts
        |   |   └── HMRContext.tsx
        │   ├── index.tsx
        │   └── App.tsx
        └── package.json
      
    • index.html: This file will serve as the entry point for your app. Add the following basic HTML structure:

        <!DOCTYPE html>
        <html lang="en">
      
        <head>
          <meta charset="utf-8" />
          <title>esbuild React App</title>
          <meta name="description" />
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          <link rel="manifest" href="./manifest.json" />
          <link rel="icon" type="image/x-icon" href="./favicon.png" />
          <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
          <link rel="stylesheet" href="./build/bundle.css" rel="preload" as="style">
          <script type="module" src="./build/bundle.js"></script>
        </head>
      
        <body>
          <noscript>You need to enable JavaScript to run this app.</noscript>
          <div id="root"></div>
        </body>
      
        </html>
      
    • index.jsx: In the src folder, create this file to serve as the entry point for your React application

        import React from 'react';
        import { createRoot } from 'react-dom/client';
      
        import App from '@/App';
        import HMRProvider from './contexts/HMRContext';
      
        const root = createRoot(document.getElementById('root') as HTMLElement)
      
        root.render(
          <React.StrictMode>
            <HMRProvider>
              <App />
            </HMRProvider>
          </React.StrictMode>
        )
      
    • App.jsx: Also in the src folder, create a simple React component to display

        import React from 'react';
      
        const App = () => {
          return (
            <div>
              <p>Hello World</p>
            </div>
          );
        };
      
        export default App
      
  5. Hot Module Replacement (HMR) and Live Reloading

    In summary, HMRProvider sets up a real-time file change detection mechanism that automatically reloads the application during development when changes are detected, enhancing the development experience by reflecting updates immediately.

     import React, { createContext, useEffect } from 'react'
    
     type Props = {
         children?: React.ReactNode,
     }
    
     const HMRContext = createContext(null);
    
     const HMRProvider = ({ children }: Props) => {
         useEffect(() => {
             if (process.env.NODE_ENV !== "production") {
                 const eventSource = new EventSource('/esbuild');
                 eventSource.addEventListener('change', (event) => {
                     const updatedFiles = JSON.parse(event.data);
    
                     if (updatedFiles.updated.length > 0) {
                         location.reload();
                     }
                 });
    
                 eventSource.onerror = (error) => {
                     console.error('EventSource error:', error);
                 };
    
                 return () => {
                     eventSource.close();
                 };
             }
         }, []);
    
         return (
             <HMRContext.Provider value={null}>
                 {children}
             </HMRContext.Provider>
         )
     }
    
     export default HMRProvider
    
  6. Creating the esbuild Configuration

    Finally, set up esbuild to bundle your React project:

    • dev.js

      This configuration sets up a robust development environment with esbuild for a React application. It handles TypeScript, CSS Modules, and various assets while providing features like live reloading, sourcemaps, and automatic browser opening, all aimed at improving the development experience.

        import { exec } from 'child_process';
        import dotenv from 'dotenv';
        import esbuild from 'esbuild';
        import CssModulesPlugin from 'esbuild-css-modules-plugin';
        import process from 'node:process';
      
        dotenv.config();
      
        const config = {
          logLevel: 'info',
          entryPoints: ['src/index.tsx'],
          outfile: 'public/build/bundle.js',
          bundle: true,
          define: {
            NODE_ENV: 'development',
          },
          minify: false,
          sourcemap: true,
          plugins: [
            CssModulesPlugin({
              force: true,
              emitDeclarationFile: true,
              localsConvention: 'camelCaseOnly',
              namedExports: true,
              inject: false,
            }),
          ],
          loader: {
            '.eot': 'dataurl',
            '.woff': 'dataurl',
            '.woff2': 'dataurl',
            '.ttf': 'dataurl',
            '.svg': 'dataurl',
            '.png': 'dataurl',
            '.jpg': 'dataurl',
            '.gif': 'dataurl',
          },
        };
      
        esbuild
          .context(config)
          .then(async (ctx) => {
            await ctx.watch();
            await ctx.serve({
              servedir: 'public',
              port: 3000,
              onRequest: ({ remoteAddress, method, path, status, timeInMS }) => {
                console.info(remoteAddress, status, `"${method} ${path}" [${timeInMS}ms]`);
              },
            });
          })
          .catch((e) => {
            console.error(e);
            process.exit(1);
          });
      
        // Open the browser after successful build
        const openBrowser = (url) => {
          switch (process.platform) {
            case 'win32':
              exec(`start ${url}`);
              break;
            case 'darwin':
              exec(`open ${url}`);
              break;
            case 'linux':
              exec(`xdg-open ${url}`);
              break;
            default:
              console.error('Unsupported platform');
          }
        };
      
        openBrowser('http://localhost:3000');
      
    • prod.js

      This script provides a comprehensive approach to building and preparing a production-ready React application with esbuild, including handling assets, minification, and ensuring that the HTML file correctly references the output files.

        import dotenv from 'dotenv';
        import esbuild from 'esbuild';
        import CssModulesPlugin from 'esbuild-css-modules-plugin';
        import { glob } from 'glob';
        import process from 'node:process';
        import sh from 'shelljs';
      
        dotenv.config();
      
        const directory = 'build';
      
        if (sh.test('-e', directory)) {
          sh.rm('-rf', directory);
        }
        sh.mkdir(directory);
        sh.cp('public/index.html', directory);
        sh.cp('public/manifest.json', directory);
        sh.cp('public/favicon.png', directory);
      
        const config = {
          logLevel: 'info',
          entryPoints: {
            main: 'src/index.tsx',
          },
          platform: 'node',
          bundle: true,
          define: {
            NODE_ENV: 'production',
          },
          minify: true,
          sourcemap: false,
          plugins: [CssModulesPlugin()],
          loader: {
            '.eot': 'dataurl',
            '.woff': 'dataurl',
            '.woff2': 'dataurl',
            '.ttf': 'dataurl',
            '.svg': 'dataurl',
            '.png': 'dataurl',
            '.jpg': 'dataurl',
            '.gif': 'dataurl',
          },
          outdir: directory,
          entryNames: 'js/[name]-[hash]',
          assetNames: 'static/[name]-[hash]',
          outExtension: {
            '.js': '.mjs',
          },
          pure: ['console.log'],
          format: 'esm',
          splitting: true,
        };
      
        const run = async () => {
          await esbuild.build(config).catch((e) => {
            console.error(e);
            process.exit(1);
          });
      
          const jsFile = await glob(`${directory}/js/*.?(m)js`, { posix: true });
          const cssFile = await glob(`${directory}/js/*.css`, { posix: true });
      
          if (jsFile.length) {
            jsFile.forEach((js) => {
              console.log(js);
              sh.sed('-i', '\./build/bundle\.js', js.replace(directory, '.'), `${directory}/index.html`);
            });
          }
      
          if (cssFile.length) {
            cssFile.forEach((css) => {
              sh.sed('-i', '\./build/bundle\.css', css.replace(directory, '.'), `${directory}/index.html`);
            });
          }
        };
      
        run();
      

Description of the Build and Development Configuration Scripts

These scripts set up and manage the development and production build processes for a React application using esbuild. They provide a comprehensive solution for both local development and preparing a production-ready build. Here’s a detailed breakdown:

  1. Development Setup
  • Environment Configuration:

    • dotenv.config() is used to load environment variables from a .env file, allowing for environment-specific settings.
  • esbuild Configuration:

    • entryPoints: Specifies the entry point for the application as src/index.tsx, indicating where esbuild should start bundling.

    • outfile: Defines the output file for the bundled JavaScript code, located in public/build/bundle.js.

    • bundle: Bundles all dependencies into a single output file.

    • define: Sets NODE_ENV to 'development', enabling development-specific features.

    • minify: Disables minification for easier debugging.

    • sourcemap: Enables source maps to map compiled code back to the original source for debugging purposes.

    • plugins: Uses the CssModulesPlugin to handle CSS Modules, with various configuration options for CSS processing.

    • loader: Configures esbuild to handle various asset types (fonts, images, etc.) by encoding them as data URLs.

  • Development Server and Live Reloading:

    • The script creates an esbuild context for efficient watch mode and incremental builds.

    • A development server is started, serving content from the public directory on port 3000.

    • onRequest: Logs HTTP requests, showing details like IP address, request method, path, status code, and response time.

    • Automatic Browser Opening: After starting the server, the script opens the application in the default web browser using platform-specific commands.

  1. Production Build
  • Directory Management:

    • Checks if a build directory exists, removes it if present, and creates a new build directory.

    • Copies static assets (index.html, manifest.json, favicon.png) to the build directory.

  • esbuild Configuration:

    • entryPoints: Specifies src/index.tsx as the entry point for the production build.

    • platform: Set to 'node', though typically 'browser' would be used for client-side React applications.

    • bundle: Bundles all dependencies into the output files.

    • define: Sets NODE_ENV to 'production', enabling production-specific optimizations.

    • minify: Enables minification to reduce file size and improve performance.

    • sourcemap: Disables sourcemaps to avoid exposing source code in the production build.

    • plugins: Uses CssModulesPlugin for handling CSS Modules with default settings.

    • loader: Handles various asset types (fonts, images, etc.) as data URLs.

    • outdir: Specifies the output directory as build.

    • entryNames and assetNames: Defines naming conventions for output files with hashed filenames for cache busting.

    • outExtension: Changes JavaScript file extensions from .js to .mjs for ECMAScript modules.

    • pure: Removes console.log statements from the code to clean up the production build.

    • format: Sets the module format to esm (ECMAScript modules).

    • splitting: Enables code splitting to optimize loading of JavaScript files.

  • Post-Build Adjustments:

    • After building, the script uses glob to locate the generated JavaScript and CSS files.

    • It updates references in index.html to point to the correct paths for the built assets.

  • Error Handling:

    • The script includes error handling to log and exit the process if the build fails.

These scripts together provide a robust setup for both development and production environments, ensuring an efficient workflow with esbuild, from local development with live reloading to optimizing and preparing the application for deployment.

Running and Building the Project

Once you’ve set up your React project with esbuild, you need to know how to run your development server and build your project for production. This section will guide you through both processes.

  • package.json:

    scripts:

    • start: Runs the development server by executing the esbuild/dev.js script.

    • build: Creates a production build by executing the esbuild/prod.js script.

    {
      "name": "esbuild-react-app",
      "version": "0.0.1",
      "license": "MIT",
      "private": true,
      "type": "module",
      "dependencies": {
        "dotenv": "^16.3.1",
        "esbuild": "^0.23.1",
        "react": "^18.2.0",
        "react-dom": "^18.2.0",
        "typescript": "^5.1.5"
      },
      "devDependencies": {
        "@types/react": "^18.2.14",
        "@types/react-dom": "^18.2.6",
        "concurrently": "^8.2.0",
        "esbuild-css-modules-plugin": "^3.1.2",
        "esbuild-jest": "^0.5.0",
        "glob": "^11.0.0",
        "jest": "^29.5.0",
        "prettier": "^2.8.8",
        "shelljs": "^0.8.5"
      },
      "scripts": {
        "start": "node esbuild/dev.js",
        "build": "node esbuild/prod.js",
        "fmt": "prettier --write"
      }
    }
  • Running the Scripts

    Start the Development Server:

      npm start
    

    Build for Production:

      npm run build
    

The package.json file with these scripts allows you to easily manage and automate the development and build processes for your React application.

In this guide, we explored how to set up a React project using esbuild, from initial configuration to running and building the project. esbuild’s speed and efficiency make it an excellent choice for modern JavaScript applications, and integrating it into your React workflow can significantly enhance both development and production processes.

Conclusion

  1. Setting Up Your Project:

    • We manually created a React project and configured esbuild for both development and production environments.

    • We set up esbuild to handle TypeScript, CSS Modules, and various asset types, tailoring the configuration to our specific needs.

  2. Development Workflow:

    • We configured a development server that supports live reloading, allowing us to see changes in real-time without manual refreshes.

    • We used esbuild’s watch mode to automatically rebuild our project as we worked on it, improving productivity.

  3. Production Build:

    • We defined a production build process that optimizes our application by minifying code, generating hashed filenames, and preparing assets for deployment.

    • We included scripts in package.json to simplify running the development server and building the project.

By following these steps, you can efficiently develop and deploy a React application using esbuild. The setup not only ensures a smooth development experience but also prepares your project for a performant production deployment.

As you continue to build and refine your React application, esbuild will help you streamline your workflow and maintain high performance. Whether you’re working on a small project or scaling up to a larger application, leveraging esbuild’s capabilities can provide a significant boost to your development process.

Feel free to reach out if you have any questions or run into issues while setting up your project. Happy coding!

Additional Resources

0
Subscribe to my newsletter

Read articles from Crispin Kalarickal directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Crispin Kalarickal
Crispin Kalarickal