Alright, let’s talk about esbuild. If you’ve ever sat there, watching your terminal churn away, waiting for tsc or Webpack to finish so you can just see if your damn code works, you’re going to love this. Esbuild is a bundler written in Go, and its primary selling point is that it is mind-bendingly fast. We’re not talking “oh, that’s nice” fast. We’re talking “wait, did it even do anything?” fast. It achieves this through a few brutally effective choices: being written in a compiled language, parallelizing everything it can, and avoiding expensive abstractions. It doesn’t do type checking—we’ll get to that—but for pure transpilation and bundling, it’s in a league of its own.

Why Speed Matters (Beyond Impatience)

You might think this is just about saving a few seconds. It’s not. It’s about feedback loops. The shorter the gap between writing code and seeing the result, the more you stay in a state of flow. A slow build process is a cognitive interruption. You alt-tab to check Twitter, you lose your train of thought, and suddenly you’re wondering what you were even building. Esbuild eliminates that. It’s so fast that it makes live-reloading servers feel truly instant. This isn’t a luxury; for a productive development environment, it’s a necessity.

The Absolute Bare Minimum Setup

Let’s get it running. First, you’ll need to install it. It’s not a TypeScript compiler itself; it’s a bundler that understands TypeScript and can transpile it to JavaScript.

npm install --save-dev esbuild

Now, let’s say you have a simple file, src/index.ts:

// src/index.ts
const message: string = "Hello, from esbuild!";
console.log(message);

To transpile this, you can use the esbuild CLI. This is the fastest way to test it out.

npx esbuild src/index.ts --outfile=dist/index.js --loader=ts

Boom. Check dist/index.js. You’ll see it’s transpiled to plain JS, stripped of the type annotation. Notice the --loader=ts flag. This tells esbuild to use its TypeScript loader for this file. It’s smart enough to usually figure this out from the .ts extension, but being explicit is good practice.

Using a Configuration File

The CLI is great for one-offs, but for a real project, you’ll want a config file. Esbuild doesn’t have a native config file format (a refreshingly direct choice). You configure it through JavaScript, typically in a build script. Create a build.js file:

// build.js
const { build } = require('esbuild');

build({
  entryPoints: ['src/index.ts'],
  outfile: 'dist/index.js',
  bundle: true, // We'll talk about this in a second
  platform: 'node', // or 'browser'
  format: 'cjs', // CommonJS format for Node
  target: 'node16', // Specify your target environment
  loader: { '.ts': 'ts' }, // Use TS loader for all .ts files
}).catch(() => process.exit(1));

Then run it with node build.js. The bundle: true option is crucial here. Without it, esbuild will just transpile each file individually. With it, esbuild will follow all your import statements, pull in those files, and create a single output file—a bundle. This is what you want for deploying a serverless function or a browser app.

The Critical Caveat: It Doesn’t Type Check

Here’s the part where I stop you before you make a huge mistake. Esbuild only transpiles TypeScript; it does not type check it. It will happily strip out your types and generate code for syntactically correct but logically broken TypeScript. This is by design; it’s what allows it to be so fast. Relying solely on esbuild is like having a brilliant compiler that’s also profoundly dyslexic.

Your build process must include a separate, parallel type-checking step. The standard way is to use tsc --noEmit.

// package.json
{
  "scripts": {
    "build": "node build.js",
    "type-check": "tsc --noEmit",
    "ci-build": "npm run type-check && npm run build"
  }
}

Never deploy code that hasn’t passed the type-check script. This is non-negotiable. The esbuild authors themselves are very clear about this. It’s not a flaw; it’s a deliberate trade-off for speed, and once you understand it, it’s a fantastic workflow.

Integrating with Your Dev Server

Where esbuild truly shines is in development. Tools like ts-node are useful but can be slow. Instead, use esbuild to transpile on the fly. For a Node.js server, you can use esbuild-runner or tsx. But the real magic is with esbuild-dev-server or using esbuild’s JavaScript API to write a simple watch script:

// watch.js
const { context } = require('esbuild');

(async () => {
  const ctx = await context({
    entryPoints: ['src/index.ts'],
    outfile: 'dist/index.js',
    bundle: true,
    platform: 'node',
    // ... all the other options
  });

  await ctx.watch(); // Enable watch mode
  console.log('Watching...');
})();

This will rebuild your dist/index.js file nearly instantaneously every time you save a source file. Pair this with nodemon dist/index.js and you have a development environment that feels like you’re working with a interpreted language. It’s that good.