DEV Community

HK Lee
HK Lee

Posted on • Originally published at pockit.tools

Rust Is Eating the JavaScript Toolchain: Rolldown, Oxc, Rspack, and the End of JS-Based Build Tools

Something wild is happening in the JavaScript ecosystem right now. The tools you use every day—your bundler, your linter, your transpiler, your minifier—are being rewritten in Rust. And they're not just a little faster. They're 10x to 100x faster.

This isn't some far-off future. Rolldown just hit 1.0 RC. Vite 8 beta ships with it by default. Oxlint is already outperforming ESLint by 50-100x. Rspack 2.0 Preview is dropping this month. If you're still running Webpack, Babel, or even esbuild, the ground under your feet is shifting fast.

In this deep dive, we'll break down what each tool does, the real-world performance numbers, how they fit together, and—most importantly—how to start migrating your projects today.


Why Rust? Why Now?

Before we get into the tools themselves, let's address why Rust specifically is taking over the JavaScript toolchain. It's not random. It's inevitable.

JavaScript Tools Have a JavaScript Problem

Here's the irony: JavaScript build tools written in JavaScript are inherently slow because they're running on a single-threaded runtime that was designed for browsers, not for CPU-intensive compilation work.

Traditional JS Toolchain Performance Bottlenecks:
┌──────────────────────────────────────────────────┐
│  Node.js (V8 Engine)                              │
│  ┌──────────────────────────────────────────────┐ │
│  │  Single-threaded event loop                  │ │
│  │  ┌────────────────────────────────────────┐  │ │
│  │  │  Garbage Collection pauses             │  │ │
│  │  │  Dynamic type checking overhead        │  │ │
│  │  │  No true parallelism (Worker threads   │  │ │
│  │  │  have serialization cost)              │  │ │
│  │  │  AST parsed multiple times by          │  │ │
│  │  │  different tools                       │  │ │
│  │  └────────────────────────────────────────┘  │ │
│  └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Consider what happens when you run npm run build on a typical React project:

  1. Babel parses your code into an AST, transforms it, serializes it back
  2. ESLint parses the same code into a different AST, runs rules
  3. Webpack/Rollup parses it again, resolves modules, bundles
  4. Terser parses the bundled output, minifies

That's the same source code being parsed 4+ times by 4 different tools, each with their own AST format, all running on a single thread, all subject to GC pauses. It's absurd when you think about it.

What Rust Brings to the Table

Rust solves these problems at the language level:

Problem JavaScript Rust
Speed Interpreted/JIT, V8 overhead Native machine code, zero-cost abstractions
Memory Garbage collected (unpredictable pauses) Ownership model (deterministic, no GC)
Parallelism Faked with worker threads (serialization overhead) True multi-threading with zero-cost Send/Sync
AST Sharing Each tool parses independently Shared AST across all operations

The result? Build times that used to take 30 seconds now take 2 seconds. Linting that took 10 seconds now takes 100 milliseconds. These aren't theoretical numbers—they're real benchmarks from production codebases.


The New Stack: Who's Building What

Let's map out the key players and how they connect:

┌─────────────────────────────────────────────────────────────┐
│                     VoidZero (Evan You)                       │
│          Unified JavaScript Toolchain in Rust                │
│                                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────────┐ │
│  │   Vite   │  │ Rolldown │  │   Oxc    │  │   Vitest    │ │
│  │ Dev Svr  │  │ Bundler  │  │ Parser   │  │ Test Runner │ │
│  │          │  │          │  │ Linter   │  │             │ │
│  │          │  │          │  │ Minifier │  │             │ │
│  │          │  │          │  │ Resolver │  │             │ │
│  └──────────┘  └──────────┘  └──────────┘  └─────────────┘ │
└─────────────────────────────────────────────────────────────┘

┌──────────────────────┐  ┌──────────────────────┐
│  Rspack (ByteDance)  │  │   Biome (Community)  │
│  Webpack-compatible  │  │  Formatter + Linter  │
│  Rust bundler        │  │  (Rome successor)    │
└──────────────────────┘  └──────────────────────┘
Enter fullscreen mode Exit fullscreen mode

The VoidZero Vision

The biggest story here is VoidZero, the company Evan You (creator of Vue.js and Vite) founded specifically to build a unified Rust-based JavaScript toolchain. The key insight: instead of having separate tools that each parse your code independently, build one shared infrastructure that does it all.

This isn't just about speed. It's about eliminating the fundamental inefficiency of the current toolchain—redundant parsing.


Rolldown: The Bundler That Changes Everything

What It Is

Rolldown is a Rust-based JavaScript/TypeScript bundler designed as a drop-in replacement for both esbuild (dev) and Rollup (production) in Vite. It just reached 1.0 Release Candidate in January 2026, which means the API is stable and it's production-ready for early adopters.

Why It Matters

Until now, Vite used a split architecture:

  • esbuild for development (fast, but limited plugin support)
  • Rollup for production builds (flexible plugins, but slow)

This split caused all sorts of headaches. Plugins had to work with both. Behavior differed between dev and prod. Debugging was a nightmare when something worked in dev but broke in production.

Rolldown eliminates this split. One bundler, both environments, Rust speed.

Real-World Benchmarks

These are actual numbers from the Rolldown team, not synthetic benchmarks:

Project: React app with 10,000 JSX components
┌──────────────────────────────────────────────┐
│  Rollup:    14.0s                            │
│  Rolldown:   3.7s  (3.8x faster)            │
└──────────────────────────────────────────────┘

Project: GitLab (large-scale monorepo)
┌──────────────────────────────────────────────┐
│  Webpack:   45.2s                            │
│  Rolldown:  12.1s  (3.7x faster)            │
└──────────────────────────────────────────────┘

Project: Excalidraw
┌──────────────────────────────────────────────┐
│  Webpack:   24.6s                            │
│  Rolldown:   1.5s  (16.4x faster)           │
└──────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Getting Started with Rolldown

If you're using Vite, migration is almost trivial. Vite 8 beta uses Rolldown by default:

# Upgrade to Vite 8 beta
npm install vite@next

# That's it. Rolldown is the default bundler now.
Enter fullscreen mode Exit fullscreen mode

For standalone usage:

// rolldown.config.js
import { defineConfig } from 'rolldown';

export default defineConfig({
  input: './src/index.ts',
  output: {
    dir: 'dist',
    format: 'esm',
    sourcemap: true,
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
  },
});
Enter fullscreen mode Exit fullscreen mode

The plugin API is Rollup-compatible, so most existing Rollup plugins work:

import { defineConfig } from 'rolldown';
import react from '@vitejs/plugin-react';

export default defineConfig({
  input: './src/main.tsx',
  plugins: [react()],
  output: {
    dir: 'dist',
    format: 'esm',
  },
});
Enter fullscreen mode Exit fullscreen mode

Rollup Plugin Compatibility

Rolldown maintains compatibility with the Rollup plugin interface. Here's how the hooks map:

// Your existing Rollup plugin works in Rolldown
const myPlugin = {
  name: 'my-plugin',

  // ✅ Supported hooks
  resolveId(source, importer) { /* ... */ },
  load(id) { /* ... */ },
  transform(code, id) { /* ... */ },
  renderChunk(code, chunk) { /* ... */ },
  generateBundle(options, bundle) { /* ... */ },

  // ✅ Build hooks
  buildStart() { /* ... */ },
  buildEnd() { /* ... */ },

  // ⚠️ Some advanced hooks may differ
  // Check rolldown.rs/docs for full compatibility matrix
};
Enter fullscreen mode Exit fullscreen mode

Oxc: One Parser to Rule Them All

What It Is

Oxc (Oxidation Compiler) is the most ambitious part of the VoidZero stack. It's not just one tool—it's a complete language toolchain:

  • Parser: 3x faster than SWC
  • Linter (Oxlint): 50-100x faster than ESLint
  • Transformer: 40x faster than Babel
  • Minifier: Matching Terser compression at native speed
  • Resolver: Module resolution at Rust speed

The secret sauce? All these tools share a single AST. Parse once, do everything. This is huge.

Oxlint: The ESLint Killer?

Let's be clear about the numbers here. Oxlint isn't just a little faster than ESLint:

Linting a large TypeScript project (~2000 files):
┌──────────────────────────────────────────────┐
│  ESLint 9:     12.4s                         │
│  Oxlint:        0.13s  (95x faster)          │
└──────────────────────────────────────────────┘

Memory usage:
┌──────────────────────────────────────────────┐
│  ESLint 9:     ~450 MB                       │
│  Oxlint:        ~28 MB  (16x less)           │
└──────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

That's not a typo. Twelve seconds versus 130 milliseconds.

Getting Started with Oxlint

Oxlint is ready for production use today:

# Install
npm install -D oxlint

# Run
npx oxlint .

# With specific rules
npx oxlint --deny-warnings --import-plugin .
Enter fullscreen mode Exit fullscreen mode

You can also run it alongside ESLint for a gradual migration:

// package.json
{
  "scripts": {
    "lint": "oxlint . && eslint . --rule '{rules-not-in-oxlint}'",
    "lint:fast": "oxlint ."
  }
}
Enter fullscreen mode Exit fullscreen mode

The recommended migration strategy: let Oxlint handle the common rules (which it does 100x faster), and keep ESLint only for project-specific or plugin-dependent rules that Oxlint doesn't cover yet.

Oxc Transformer: Goodbye Babel

The Oxc transformer replaces Babel for most common use cases:

# Current: Babel transforms (slow)
npx babel src --out-dir dist

# Future: Oxc transforms (40x faster)
# Already available via Rolldown and Vite 8
Enter fullscreen mode Exit fullscreen mode

Common Babel transforms that Oxc already handles:

  • JSX transformation (React, Preact)
  • TypeScript stripping
  • Decorators (Stage 3)
  • Optional chaining lowering
  • Nullish coalescing lowering

What you might still need Babel for:

  • Custom Babel plugins (rare in 2026)
  • Very legacy browser targets (IE11... please stop)

Rspack: The Webpack Escape Hatch

What It Is

Rspack, built by ByteDance, is a different bet. While Rolldown aims to replace Rollup/esbuild, Rspack aims to be a drop-in replacement for Webpack. If you have a massive Webpack config that's evolved over 5 years, Rspack is your migration path.

Why It Matters

Let's be real: millions of production applications still run Webpack. Many have complex configs with dozens of plugins, custom loaders, and hard-to-understand optimization splits. Rewriting everything for Vite isn't always practical.

Rspack says: "Keep your webpack.config.js. We'll just make it fast."

Benchmarks

Large enterprise project (ByteDance internal):
┌──────────────────────────────────────────────┐
│  Webpack 5:   120s  (cold build)              │
│  Rspack 1.7:   36s  (70% faster)             │
│                                               │
│  Webpack 5:    8.2s (HMR update)              │
│  Rspack 1.7:   1.6s (80% faster)             │
└──────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Getting Started with Rspack

The migration from Webpack is designed to be minimal:

// rspack.config.js (look familiar?)
const { defineConfig } = require('@rspack/cli');

module.exports = defineConfig({
  entry: './src/index.tsx',
  output: {
    path: __dirname + '/dist',
    filename: '[name].[contenthash].js',
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: {
          loader: 'builtin:swc-loader',  // Built-in SWC, no extra install
          options: {
            jsc: {
              parser: { syntax: 'typescript', tsx: true },
              transform: { react: { runtime: 'automatic' } },
            },
          },
        },
      },
      {
        test: /\.css$/,
        type: 'css',  // Built-in CSS support
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
  },
  optimization: {
    splitChunks: {
      chunks: 'all',  // Same webpack API
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Key differences from Webpack:

  • Use builtin:swc-loader instead of babel-loader (built into Rspack)
  • CSS modules work natively with type: 'css/module'
  • Most webpack plugins have Rspack equivalents
  • @rspack/plugin-react-refresh for HMR

Webpack Plugin Compatibility

Rspack supports many Webpack plugins out of the box:

const { HtmlRspackPlugin, CopyRspackPlugin } = require('@rspack/core');

module.exports = {
  plugins: [
    new HtmlRspackPlugin({ template: './index.html' }),
    new CopyRspackPlugin({ patterns: [{ from: 'public' }] }),
    // Many webpack plugins "just work"
  ],
};
Enter fullscreen mode Exit fullscreen mode

Rspack 2.0 Preview (February 2026)

The upcoming Rspack 2.0 brings:

  • Persistent caching: Cache across builds for even faster cold starts
  • Module Federation 2.0: Improved micro-frontend support
  • Better tree shaking: Approaching Rolldown-level dead code elimination
  • Oxc integration: Using Oxc for parsing and transformation

Head-to-Head: Which Tool for Which Situation?

Here's the decision matrix:

Migration Paths

Your Current Setup          →    Recommended Migration
────────────────────────────────────────────────────────
Webpack + Babel + ESLint    →    Rspack + Oxlint (easiest)
                            →    Vite 8 + Rolldown (if you can restructure)

Vite + Rollup + ESLint     →    Vite 8 + Rolldown + Oxlint (almost free)

esbuild (custom)           →    Rolldown (API compatible for most cases)

Create React App           →    Vite 8 (CRA is dead, just migrate)
Enter fullscreen mode Exit fullscreen mode

Feature Comparison

Feature Rolldown Rspack esbuild Webpack
Speed (cold) ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐
Speed (HMR) ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐
Plugin ecosystem ⭐⭐⭐⭐ (Rollup compat) ⭐⭐⭐⭐⭐ (Webpack compat) ⭐⭐ ⭐⭐⭐⭐⭐
Code splitting ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐
Tree shaking ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
Config complexity Low Medium (webpack-like) Low High
Production ready RC (1.0 imminent) Yes (1.7) Yes Yes

The Linter Comparison

Feature Oxlint ESLint 9 Biome
Speed ⭐⭐⭐⭐⭐ (100x) ⭐⭐ ⭐⭐⭐⭐
Rule coverage ~500 rules ~300+ rules + plugins ~300 rules
Custom rules Not yet Yes (JS plugins) Not yet
Auto-fix Partial Yes Yes
Formatting No (use Prettier/Biome) No Yes (built-in)
Config Minimal Complex (.eslintrc) Simple

Practical Migration Guide

Step 1: Win the Quick Wins (Linting)

Start with linting because it's zero-risk and the biggest ROI:

# Install Oxlint
npm install -D oxlint

# Add to CI pipeline first (no risk)
# .github/workflows/ci.yml
# - name: Lint (fast)
#   run: npx oxlint .

# Update package.json
{
  "scripts": {
    "lint": "oxlint . && eslint .",
    "lint:ci": "oxlint ."
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Evaluate Your Bundler Situation

If you're on Vite (easiest path):

# You're one upgrade away
npm install vite@next

# Check vite.config.ts — most configs work as-is
# Test thoroughly in staging
npm run build
npm run preview
Enter fullscreen mode Exit fullscreen mode

If you're on Webpack (evaluate Rspack):

# Install Rspack
npm install -D @rspack/core @rspack/cli

# Rename webpack.config.js → rspack.config.js
# Replace require('webpack') → require('@rspack/core')
# Replace babel-loader → builtin:swc-loader
# Test with: npx rspack build
Enter fullscreen mode Exit fullscreen mode

If you're on Next.js:

Good news: Next.js has its own Rust-based compiler (SWC + Turbopack). You're partially on the Rust train already. Watch for deeper Oxc integration coming later in 2026.

Step 3: Measure Everything

Before and after any migration, measure:

// build-benchmark.mjs
import { performance } from 'perf_hooks';
import { execSync } from 'child_process';

const runs = 5;
const times = [];

for (let i = 0; i < runs; i++) {
  // Clear caches
  execSync('rm -rf dist .cache node_modules/.cache');

  const start = performance.now();
  execSync('npm run build', { stdio: 'pipe' });
  const end = performance.now();

  times.push(end - start);
  console.log(`Run ${i + 1}: ${((end - start) / 1000).toFixed(2)}s`);
}

const avg = times.reduce((a, b) => a + b) / times.length;
const min = Math.min(...times);
const max = Math.max(...times);

console.log(`\nAverage: ${(avg / 1000).toFixed(2)}s`);
console.log(`Min: ${(min / 1000).toFixed(2)}s | Max: ${(max / 1000).toFixed(2)}s`);
Enter fullscreen mode Exit fullscreen mode

Common Migration Pitfalls

Pitfall 1: Plugin Incompatibility

Not every Webpack loader or Rollup plugin has a Rust equivalent yet. Check compatibility first:

# For Rspack: Check the compatibility table
# https://rspack.dev/guide/compatibility

# For Rolldown: Check Rollup plugin compat
# https://rolldown.rs/contrib-guide/plugin-compat
Enter fullscreen mode Exit fullscreen mode

Common issues:

  • sass-loader → Use Rspack's built-in sass support or builtin:lightningcss-loader
  • postcss-loader → Works in both Rspack and Rolldown, but check plugin compat
  • Custom Babel plugins → May need rewriting or alternative approach

Pitfall 2: Assuming Identical Output

Rust bundlers produce slightly different output than their JS counterparts. This rarely matters, but test:

# Compare bundle sizes
du -sh dist-webpack/ dist-rspack/

# Compare bundle content (look for missing exports)
npx source-map-explorer dist/main.*.js
Enter fullscreen mode Exit fullscreen mode

Pitfall 3: CI/CD Cache Invalidation

New tools = new cache keys. Update your CI config:

# .github/workflows/ci.yml
- uses: actions/cache@v4
  with:
    path: |
      node_modules/.cache
      .rspack-cache      # Add Rspack cache
    key: ${{ runner.os }}-build-${{ hashFiles('**/package-lock.json') }}
Enter fullscreen mode Exit fullscreen mode

The Future: What's Coming Next

Q1 2026 (Now)

  • ✅ Rolldown 1.0 RC (January 2026)
  • ✅ Vite 8 beta with Rolldown default
  • ✅ Rspack 1.7 (final before 2.0)
  • 🔜 Rspack 2.0 Preview (February 2026)

Q2 2026

  • Rolldown 1.0 stable release expected
  • Vite 8 stable with Rolldown
  • Oxc minifier stable release
  • Oxlint expanding rule coverage to match ESLint core

H2 2026

  • Full VoidZero stack integration (Vite + Rolldown + Oxc + Vitest)
  • Rspack 2.0 stable with persistent caching
  • TypeScript-aware bundling (no more separate tsc step)

The Endgame

The ultimate vision—particularly from VoidZero—is a single vite build command that:

  1. Parses your code once (Oxc)
  2. Lints it (Oxlint, using the same AST)
  3. Transforms TypeScript/JSX (Oxc transformer, same AST)
  4. Bundles it (Rolldown, same AST)
  5. Minifies it (Oxc minifier, same AST)
  6. Tests it (Vitest, integrated)

One AST. One pass. No redundant work. That's the promise.


Conclusion

The Rust-ification of JavaScript tooling isn't a trend—it's a tectonic shift. The numbers are too dramatic to ignore:

  • Build times: 4-16x faster
  • Linting: 50-100x faster
  • Memory usage: 10-16x less
  • Developer experience: Noticeably snappier HMR and dev server

Here's what to do right now:

  1. Today: Install Oxlint alongside your existing linter. It takes 2 minutes and you'll immediately feel the difference.
  2. This week: If you're on Vite, try upgrading to Vite 8 beta in a branch. It's nearly a zero-config migration.
  3. This month: If you're on Webpack, evaluate Rspack on a non-critical project. The migration is straightforward.
  4. This quarter: Plan your full migration strategy. By mid-2026, the Rust toolchain will be the default.

The old tools aren't going anywhere immediately—Webpack and Babel will be maintained for years. But the performance gap is so large that staying on them is becoming a competitive disadvantage. Your CI bills are higher, your developer experience is slower, and your team is spending time waiting instead of building.

The future of JavaScript tooling is Rust. The migration window is now.


🔒 Privacy First: This article was originally published on the Pockit Blog.

Stop sending your data to random servers. Use Pockit.tools for secure utilities, or install the Chrome Extension to keep your files 100% private and offline.

Top comments (0)