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 │ │ │
│ │ └────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
Consider what happens when you run npm run build on a typical React project:
- Babel parses your code into an AST, transforms it, serializes it back
- ESLint parses the same code into a different AST, runs rules
- Webpack/Rollup parses it again, resolves modules, bundles
- 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) │
└──────────────────────┘ └──────────────────────┘
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) │
└──────────────────────────────────────────────┘
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.
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'],
},
});
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',
},
});
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
};
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) │
└──────────────────────────────────────────────┘
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 .
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 ."
}
}
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
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) │
└──────────────────────────────────────────────┘
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
},
},
});
Key differences from Webpack:
- Use
builtin:swc-loaderinstead ofbabel-loader(built into Rspack) - CSS modules work natively with
type: 'css/module' - Most webpack plugins have Rspack equivalents
-
@rspack/plugin-react-refreshfor 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"
],
};
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)
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 ."
}
}
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
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
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`);
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
Common issues:
-
sass-loader→ Use Rspack's built-insasssupport orbuiltin: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
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') }}
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
tscstep)
The Endgame
The ultimate vision—particularly from VoidZero—is a single vite build command that:
- Parses your code once (Oxc)
- Lints it (Oxlint, using the same AST)
- Transforms TypeScript/JSX (Oxc transformer, same AST)
- Bundles it (Rolldown, same AST)
- Minifies it (Oxc minifier, same AST)
- 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:
- Today: Install Oxlint alongside your existing linter. It takes 2 minutes and you'll immediately feel the difference.
- This week: If you're on Vite, try upgrading to Vite 8 beta in a branch. It's nearly a zero-config migration.
- This month: If you're on Webpack, evaluate Rspack on a non-critical project. The migration is straightforward.
- 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)