Comparisons

esbuild vs SWC: AI Rules for JavaScript Tooling

esbuild (Go-based) and SWC (Rust-based) are the two fastest JavaScript transform tools. Each powers different frameworks and tools. AI rules for when each is used, configuration differences, and framework-specific integration.

5 min read·July 5, 2025

You use esbuild or SWC every day without knowing it. Vite = esbuild. Next.js = SWC. The framework chooses for you.

Framework-to-engine mapping, decorator support difference, direct config when needed, and why the framework rule is enough

The Tools You Use Without Knowing

esbuild and SWC are: the fast JavaScript/TypeScript transform engines that power modern development tools. Most developers: never configure either directly. They are: used behind the scenes by frameworks and build tools. esbuild (written in Go): powers Vite's dev server (transforms files on the fly), Bun's bundler, and tsx (TypeScript execution). SWC (written in Rust): powers Next.js's compiler (replaces Babel for faster builds), Parcel's transformer, and Deno's TypeScript support. You choose: the framework or tool. The transform engine: comes with it.

Why this comparison matters for AI rules: the AI must know which transform engine the project uses to understand: which TypeScript features are supported (esbuild and SWC handle TypeScript slightly differently), which JSX transform is used (React 17+ automatic runtime vs classic runtime), and which configuration file to reference (esbuild: part of vite.config.ts. SWC: .swcrc or next.config.js compiler section). The transform engine affects: how TypeScript decorators work, how JSX is compiled, and which syntax features are available.

The practical rule: in most projects, the AI does not need to configure esbuild or SWC directly. The framework handles it. But the AI should know: which engine the framework uses, to understand: transform behavior, TypeScript support level, and where to look for transform-related configuration.

Framework-to-Engine Mapping

esbuild is used by: Vite (dev server transforms — production uses Rollup), Bun (built-in bundler and transpiler), tsx/tsup (TypeScript execution and packaging), and standalone esbuild CLI (esbuild src/index.ts --bundle --outdir=dist). When your project uses Vite: esbuild handles TypeScript and JSX transforms in development. AI rule: 'Vite project: esbuild handles transforms in dev. TypeScript: stripped (no type checking — use tsc --noEmit separately). JSX: automatic runtime (React 17+).'

SWC is used by: Next.js (compiler replaces Babel since Next.js 12+), Parcel (v2 uses SWC for transforms), Deno (TypeScript compilation), and standalone @swc/cli. When your project uses Next.js: SWC handles all transforms (TypeScript, JSX, CSS modules, optimization). AI rule: 'Next.js project: SWC handles transforms. TypeScript: stripped (use tsc for type checking). Decorators: supported with experimentalDecorators in tsconfig. next.config.js compiler section for SWC options.'

The mapping rule: the AI does not need a direct esbuild or SWC rule in most projects. The framework rule ("This project uses Vite" or "This project uses Next.js") implicitly determines: the transform engine. The AI should understand: Vite = esbuild in dev, Next.js = SWC. This understanding affects: how the AI configures TypeScript options, how it handles decorators, and what transform limitations to expect.

  • esbuild: Vite (dev), Bun (bundler), tsx (TypeScript exec), standalone CLI
  • SWC: Next.js (compiler), Parcel (v2), Deno (TS compilation), standalone @swc/cli
  • Framework determines engine: Vite = esbuild. Next.js = SWC. No direct config usually needed
  • Both: strip TypeScript types (no type checking — use tsc --noEmit separately)
  • AI rule: the framework rule implicitly determines the transform engine
💡 One Rule to Remember

Vite = esbuild (dev transforms). Next.js = SWC (all transforms). Bun = esbuild (bundler). Parcel = SWC (transformer). Deno = SWC (TypeScript). The framework or runtime you chose already determined the transform engine. You do not pick esbuild or SWC directly — the tool picks for you.

Transform Capabilities: What Each Handles Differently

TypeScript handling: both esbuild and SWC strip TypeScript types without checking them (they are transpilers, not type checkers). Difference: esbuild does not support TypeScript decorators (the legacy emitDecoratorMetadata option). SWC does support decorators (both legacy and the TC39 proposal). For projects using: NestJS (requires decorators), TypeORM (uses decorators for entities), or Angular (decorator-based): SWC is necessary. AI rule: 'If using TypeScript decorators: SWC (Next.js, @swc/cli). esbuild does not support emitDecoratorMetadata.'

JSX handling: both support automatic and classic JSX transforms. esbuild: configured via jsx option ('automatic' for React 17+). SWC: configured via .swcrc jsc.transform.react section or Next.js handles it automatically. In practice: both handle React JSX identically for typical projects. The difference: only visible in edge cases (custom JSX factories, Preact h() function configuration).

CSS handling: esbuild has basic CSS bundling (import './styles.css' works). SWC does not handle CSS (Next.js uses its own CSS pipeline, not SWC). For CSS: the framework's CSS handling (Vite CSS, Next.js CSS modules, Tailwind PostCSS) is more relevant than the transform engine. The AI should: reference the framework's CSS documentation, not the transform engine's.

  • TypeScript: both strip types without checking. Use tsc --noEmit for type checking
  • Decorators: SWC supports (emitDecoratorMetadata). esbuild does not — critical for NestJS, TypeORM
  • JSX: both support automatic and classic transforms. Identical for typical React projects
  • CSS: esbuild has basic CSS. SWC does not handle CSS. Framework CSS pipeline is what matters
  • In practice: the differences rarely affect day-to-day coding. Decorators: the main exception
⚠️ Decorator Support Is the Critical Difference

If your project uses TypeScript decorators (NestJS @Controller, TypeORM @Entity, Angular @Component): esbuild cannot handle emitDecoratorMetadata. SWC can. This is not a minor feature gap — it determines whether the project can use esbuild-based tools at all. NestJS + Vite dev server: will fail on decorators. NestJS + SWC: works.

When You Configure Them Directly

Direct esbuild config: when using esbuild standalone (not via Vite). esbuild.build({ entryPoints: ['src/index.ts'], bundle: true, outdir: 'dist', platform: 'node', target: 'node20' }). Use case: building a Node.js library, CLI tool, or serverless function without a framework. AI rule: 'Standalone esbuild: esbuild.build() in a build script. Platform: node for server, browser for client. Target: the minimum supported runtime version. Bundle: true for single-file output.'

Direct SWC config: .swcrc at the project root or via @swc/cli. { "jsc": { "parser": { "syntax": "typescript", "decorators": true }, "transform": { "legacyDecorator": true } } }. Use case: when the framework does not handle SWC (custom Parcel setup, standalone TypeScript compilation). In Next.js: SWC is configured via next.config.js compiler: { ... } section (not .swcrc). AI rule: 'SWC in Next.js: next.config.js compiler section. Standalone: .swcrc. Custom: @swc/core programmatic API.'

For most projects: the AI never needs to write esbuild or SWC configuration directly. The framework (Vite, Next.js, Bun) handles it. The exception: building standalone tools, libraries, or serverless functions outside a framework. The AI rule: "Transform tool: handled by the framework. Direct configuration: only for standalone build scripts or library packaging. When in doubt: use the framework's configuration, not the engine's."

  • Most projects: framework handles the engine. No direct config needed
  • Standalone esbuild: for libraries, CLI tools, serverless functions without a framework
  • Standalone SWC: .swcrc or @swc/core API for custom Parcel or non-framework TypeScript
  • Next.js SWC: configured via next.config.js compiler section (not .swcrc)
  • When in doubt: use the framework config (vite.config.ts, next.config.js), not the engine config
ℹ️ Direct Config = No Framework

You only write esbuild.build() or .swcrc when building outside a framework: standalone CLI tools, serverless functions, library packages, custom build scripts. If you are using Vite, Next.js, or any framework: the framework config (vite.config.ts, next.config.js) is the right place for transform options. Direct engine config and framework config should never coexist for the same files.

Transform Tool Summary

Summary of esbuild vs SWC AI rules.

  • esbuild (Go): Vite dev server, Bun bundler, tsx. SWC (Rust): Next.js compiler, Parcel, Deno
  • Both: strip TypeScript types without checking. Use tsc --noEmit for type checking
  • Decorators: SWC supports (NestJS, TypeORM). esbuild does not. Critical framework difference
  • JSX: both handle React automatic transform identically for typical projects
  • Framework determines engine: Vite = esbuild. Next.js = SWC. No direct config usually needed
  • Direct config: only for standalone tools, libraries, serverless functions outside a framework
  • Most projects: the AI references the framework config, not the engine config
  • The framework rule in CLAUDE.md: implicitly determines which transform engine is used