← Back to DevBytes

Migrating from esbuild to swc: Step-by-Step Guide

What Are esbuild and SWC?

esbuild is an extremely fast JavaScript bundler, minifier, and transpiler written in Go. It handles TypeScript, JSX, and modern syntax out of the box and can bundle entire applications with a single command. SWC (Speedy Web Compiler) is a Rust-based platform for fast transpilation and compilation of JavaScript/TypeScript. SWC focuses on transforming code (parsing, emitting target-specific JavaScript) and is typically used as a compiler rather than a full bundler. Both tools leave traditional solutions like Babel and webpack’s default loaders far behind in terms of raw speed.

While esbuild can both bundle and transpile, SWC is primarily a transpiler that integrates deeply into higher‑level tools: you’ll find it inside Next.js, as a webpack loader (swc-loader), as a Jest transformer (@swc/jest), or as a Node.js runtime hook (@swc-node/register). Understanding this distinction is key to a successful migration.

Why Migrate from esbuild to SWC?

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

SWC has become the default compiler in major frameworks like Next.js and is increasingly adopted across the JavaScript ecosystem. Migrating gives you:

If you currently rely on esbuild only for transpilation (e.g., via esbuild-loader in webpack, esbuild-jest, or esbuild-register), the switch to SWC is straightforward and unlocks these benefits without sacrificing speed.

Step-by-Step Migration Guide

1. Replace esbuild-loader with swc-loader in Webpack

Remove esbuild-loader from your webpack configuration and install swc-loader and @swc/core:

npm install --save-dev @swc/core swc-loader

Then update your webpack.config.js rule for TypeScript/TSX files.

Before (esbuild-loader):


// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'esbuild-loader',
          options: {
            loader: 'tsx',
            target: 'es2015',
          },
        },
      },
    ],
  },
};

After (swc-loader):


// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'swc-loader',
          options: {
            jsc: {
              parser: {
                syntax: 'typescript',
                tsx: true,
              },
              target: 'es2015',
            },
            module: {
              type: 'es6',   // or 'commonjs' depending on your output
            },
          },
        },
      },
    ],
  },
};

You can extract the options into a shared .swcrc file and use configFile: true in the loader options to avoid duplication.

2. Migrate Jest Transformer from esbuild-jest to @swc/jest

Remove esbuild-jest and install @swc/jest and @swc/core:

npm install --save-dev @swc/core @swc/jest

Update your Jest configuration. If you previously used esbuild-jest as the transformer:

Before:


// jest.config.js
module.exports = {
  transform: {
    '^.+\\.tsx?$': 'esbuild-jest',
  },
};

After:


// jest.config.js
module.exports = {
  transform: {
    '^.+\\.tsx?$': ['@swc/jest', {
      jsc: {
        parser: {
          syntax: 'typescript',
          tsx: true,
        },
        target: 'es2021',
      },
      module: {
        type: 'commonjs',  // Jest runs in Node.js CJS mode
      },
    }],
  },
};

Alternatively, create a .swcrc at the project root and point Jest to it:


// jest.config.js
module.exports = {
  transform: {
    '^.+\\.tsx?$': ['@swc/jest', { configFile: true }],
  },
};

Then populate .swcrc with the same options as above.

3. Switch Node.js Runtime Compilation from esbuild-register to @swc-node/register

If you use esbuild-register to run TypeScript files directly (e.g., scripts, servers, or development tools), replace it with @swc-node/register. Install the packages:

npm install --save-dev @swc-node/register @swc/core

Change your Node.js -r flag from esbuild-register to @swc-node/register. For example, in package.json:

Before:


"scripts": {
  "start": "node -r esbuild-register src/index.ts"
}

After:


"scripts": {
  "start": "node -r @swc-node/register src/index.ts"
}

The runtime hook reads a .swcrc file by default. Provide a configuration that matches your Node.js target:


// .swcrc
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "decorators": true
    },
    "target": "es2021",
    "transform": {
      "legacyDecorator": true,
      "decoratorMetadata": true
    }
  },
  "module": {
    "type": "commonjs"
  }
}

If you use ES modules, set "module.type": "es6" and run Node with --loader instead (consult @swc-node/register documentation).

4. Update Scripts and Configuration Files

Remove esbuild‑specific dependencies:

npm uninstall esbuild esbuild-loader esbuild-jest esbuild-register

Install the SWC equivalents:

npm install --save-dev @swc/core @swc/cli swc-loader @swc/jest @swc-node/register

If your project used esbuild CLI for transpilation (not bundling), you can replace it with swc CLI. For example, to compile a source directory to an output directory:


npx swc src -d dist --config-file .swcrc

If you relied on esbuild for bundling, note that SWC does not bundle by itself. You’ll need a dedicated bundler like webpack, Rollup, or the experimental swcpack. A common pattern is to use webpack with swc-loader as described in step 1. Update your package.json scripts accordingly, pointing the build step to webpack (or rollup) instead of esbuild.

5. Handle Custom Plugins and Edge Cases

esbuild plugins (e.g., for importing CSS, images, or applying custom transformations) do not have direct counterparts in SWC’s plugin system, which is still experimental and Rust‑based. You’ll need to replace these with bundler‑side solutions:

SWC’s built‑in transforms cover most TypeScript and JSX needs. If you previously relied on esbuild plugins for non‑standard syntax, evaluate whether SWC’s native plugins (e.g., @swc/plugin-emotion) can replace them. Otherwise, fall back to Babel or bundler plugins for those specific transformations.

Best Practices for a Smooth Migration

Conclusion

Migrating from esbuild to SWC opens the door to a Rust‑powered compilation pipeline that integrates seamlessly with modern JavaScript tooling. By replacing the esbuild‑based loader, Jest transformer, and Node runtime hook with their SWC equivalents, you gain a unified configuration model, broader plugin support, and native performance that scales with your project. Follow the incremental steps, lean on a shared .swcrc file, and keep your type checker running separately. The result is a faster, more maintainable build setup that aligns with the direction of the entire ecosystem.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles