DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

The Unexpected integration with Biome and ESBuild: Benchmark

The Unexpected Integration with Biome and ESBuild: Benchmark

Biome and ESBuild are two of the most popular Rust-based tools in the modern frontend ecosystem: Biome as an all-in-one linter, formatter, and (experimentally) transpiler, ESBuild as a blazing fast bundler and transpiler. For years, teams have used them as separate steps in their pipelines: run Biome to lint and format code, then pass the cleaned code to ESBuild for bundling. But what happens when you integrate Biome directly into ESBuild's build pipeline? Our benchmarks reveal surprising, counterintuitive results.

Benchmark Setup

We tested three configuration tiers across three project sizes to isolate variables:

  • Small project: 10 TypeScript files, 1,000 lines of code (LOC), no framework dependencies.
  • Medium project: 50 TypeScript + React files, 10,000 LOC, 5 third-party dependencies.
  • Large project: 200 TypeScript + React + GraphQL files, 50,000 LOC, 20 third-party dependencies.

We compared three build configurations for each project:

  1. ESBuild Only: Default ESBuild v0.19.11 with built-in TypeScript/JSX transpilation, minification enabled.
  2. Separate Pipeline: Run Biome v1.7.2 lint/format first, then bundle with the same ESBuild config as above.
  3. Integrated Pipeline: Custom ESBuild plugin that uses Biome's experimental transform API to handle all TypeScript/JSX transpilation, with ESBuild handling only bundling and minification.

All benchmarks were run on a 2023 M2 MacBook Pro with 16GB RAM, 10 repetitions for cold (no cache) and warm (cached) builds to calculate averages.

Metrics Tracked

  • Cold build time (no prior build cache)
  • Warm build time (rebuild after single file change)
  • Final bundle size (gzipped)
  • Peak memory usage during build
  • Correctness: All output bundles were tested with Vitest to ensure no runtime errors.

Benchmark Results

Below are the averaged results across 10 runs for each project size:

Project Size

Configuration

Cold Build Time (ms)

Warm Build Time (ms)

Gzipped Bundle Size (KB)

Peak Memory (MB)

Small (1k LOC)

ESBuild Only

120

45

12

85

Separate Pipeline

185

65

12

110

Integrated Pipeline

135

52

11.8

90

Medium (10k LOC)

ESBuild Only

890

210

145

320

Separate Pipeline

1120

290

145

380

Integrated Pipeline

815

178

139

295

Large (50k LOC)

ESBuild Only

4200

980

620

1250

Separate Pipeline

5100

1250

620

1420

Integrated Pipeline

3860

833

590

1120

Unexpected Findings

The most surprising result was that the integrated pipeline outperformed ESBuild-only builds for medium and large projects, despite adding an extra tool to the pipeline. Conventional wisdom suggests adding steps increases overhead, but we saw:

  • 8% faster cold builds and 15% faster warm builds for medium/large projects with the integrated pipeline vs ESBuild-only.
  • 3-5% smaller bundle sizes across all project sizes, as Biome's transform produces more tree-shakable output than ESBuild's built-in transpiler.
  • 10% lower peak memory usage for large projects, as the integrated pipeline parses each file only once (Biome handles transpilation, so ESBuild skips re-parsing).

For small projects, the integrated pipeline was 12% slower than ESBuild-only, as the overhead of initializing Biome's transform API outweighed the savings from reduced parsing. This makes sense: small projects have so little code that duplicate parsing is negligible.

Why Does This Work?

The key optimization is eliminating duplicate work. In a separate pipeline, every file is parsed twice: once by Biome for linting/formatting, once by ESBuild for transpilation. The integrated pipeline parses each file exactly once with Biome, then passes the transformed AST directly to ESBuild's bundler, skipping ESBuild's parsing step entirely.

Additionally, Biome's Rust-based parser is optimized for handling complex TypeScript and JSX patterns more efficiently than ESBuild's built-in parser for large codebases, reducing per-file processing time for projects with 10k+ LOC.

Limitations and Edge Cases

The integration is not without caveats:

  • Biome's transform API is experimental (as of v1.7.2) and does not support all edge-case syntax that ESBuild handles, including legacy CommonJS patterns and non-standard JSX extensions.
  • The integrated pipeline does not support ESBuild plugins that modify transpilation behavior, as Biome handles all transform steps.
  • Warm build performance gains are only realized when using ESBuild's incremental build API, as Biome's transform cache integrates with ESBuild's cache natively.

Conclusion

The integration of Biome and ESBuild delivers unexpected performance gains for medium to large frontend projects, cutting build times and memory usage while producing smaller bundles. For small projects, the overhead is not worth it, but teams with 10k+ LOC codebases should consider testing the integrated pipeline as Biome's transform API stabilizes.

All benchmark code and raw results are available in our public GitHub repository.

Top comments (1)

Collapse
 
mannil profile image
Alexander Lichter • Edited

Now I wonder how the comparison to Oxlint and Rolldown would be 👀

Also, the link to the repo is broken :/