The Rust JS Tooling Ecosystem
OXC, Rolldown, Vite, SWC, Turbopack, Biome — the complete picture of why the JavaScript ecosystem is rewriting its tools in Rust (and Go), what each tool does, and what you actually need to care about as a developer.
Why is everyone rewriting JS tools in Rust?
JavaScript build tools are bottlenecks. A slow build costs every developer minutes per day, hours per week, and hours per CI run. At scale (monorepos, large teams), this becomes existential. Native languages like Rust and Go eliminate the overhead that comes with running tools on V8.
The fundamental problem with JS-based tooling
1. Node.js starts up (~50–200ms overhead)
2. V8 JIT warms up (startup cost per run)
3. Read 10,000 .ts files (synchronous fs in JS)
4. Parse each file (JS parser in JS = slow)
5. Transform/lint (single-threaded JS)
6. Bundle output (more JS, more GC pauses)
7. Write output files
// Result: webpack cold build ~60-180s on large projects
1. Native binary starts (~1ms)
2. Read 10,000 .ts files (parallel async I/O)
3. Parse each file (Rust parser, multi-core)
4. Transform/lint (multi-threaded)
5. Bundle output (zero GC pauses)
6. Write output files
// Result: OXC parses 3x faster than esbuild (Go)
// Rolldown full build in seconds
Why Rust specifically (not just C++)?
- Memory safety without GC: No garbage collector pauses, no segfaults, no data races — enforced at compile time (same ownership system from the Rust guide)
- Fearless concurrency: Parse 10,000 files in parallel across all CPU cores with no data race bugs
- C-level speed: Benchmarks show Rust tools often faster than equivalent C++ due to better optimization hints
- Interop with JS:
napi-rsandwasm-bindgenmake it easy to call Rust from Node.js or the browser - Great ecosystem: crates for parsing, async I/O (tokio), WASM output
Ecosystem Map — Old vs New
Every JS/TS build tool and its native replacement.
How We Got Here — Timeline
OXC — The Oxidation Compiler Rust
OXC is a collection of high-performance Rust tools for JavaScript and TypeScript built on a shared parser. Think of it as the "foundation layer" — Rolldown is built on OXC, Oxlint is built on OXC.
OXC components
# Install oxlint
npm install -D oxlint
# Run on your project (no config required)
npx oxlint src/
# With config
# oxlintrc.json
{
"rules": {
"no-unused-vars": "error",
"no-console": "warn"
}
}
# Recommended: run oxlint FIRST (fast), then ESLint for complex rules
# "lint:fast": "oxlint src/",
# "lint": "oxlint src/ && eslint src/"
Rolldown Rust
Rolldown is a Rust bundler with a Rollup-compatible plugin API. It's the centerpiece of the "VoidZero" initiative (Evan You's company to build a unified JS toolchain). Rolldown replaces both esbuild and Rollup inside Vite.
Rolldown vs Rollup vs esbuild
| Feature | Rollup JS | esbuild Go | Rolldown Rust |
|---|---|---|---|
| Speed | Slow | Very fast | Fastest |
| Tree-shaking | Excellent | Good | Excellent (Rollup model) |
| Plugin ecosystem | Huge (Rollup plugins) | Limited | Rollup-compatible ✅ |
| Code splitting | Yes | Yes | Yes |
| CJS output | Yes | Yes | Yes |
| ESM output | Yes | Yes | Yes |
| Dev server | No | Via Vite | Via Vite (replaces esbuild here) |
| Language | JavaScript | Go | Rust (built on OXC) |
Key architectural insight
Rolldown doesn't just replicate Rollup's output — it uses the same mental model (entry → chunks → tree-shaken output) but implements it in Rust with OXC as the parser. Your existing Vite/Rollup plugins continue to work because Rolldown exposes the same hook API.
// A Rolldown plugin looks identical to a Rollup plugin
import { defineConfig } from 'rolldown'; // or 'rollup'
export default defineConfig({
input: './src/index.ts',
output: { dir: 'dist', format: 'esm' },
plugins: [
{
name: 'my-plugin',
// Same hooks as Rollup
transform(code, id) {
if (!id.endsWith('.ts')) return;
// transform TypeScript...
return { code: transformed };
},
resolveId(source) { /* ... */ },
load(id) { /* ... */ },
generateBundle(options, bundle) { /* ... */ },
}
]
});
Vite's Evolution Vite 6+
Vite is the most important frontend build tool. Understanding how it's changing helps you understand why Rolldown and OXC matter.
Vite before (dual-bundler problem)
┌────────────────────────────────────────────────────────┐
│ vite dev │
│ └─ esbuild (Go) — pre-bundles node_modules deps │
│ └─ Native ESM — serves your source files as-is │
│ │
│ vite build │
│ └─ Rollup (JS) — bundles everything for production │
│ │
│ Problem: dev uses esbuild, prod uses Rollup │
│ → subtle differences in output, hard to debug │
│ → two plugin systems to maintain │
└────────────────────────────────────────────────────────┘
Vite after Rolldown
┌────────────────────────────────────────────────────────┐
│ vite dev │
│ └─ Rolldown (Rust) — pre-bundles + serves │
│ │
│ vite build │
│ └─ Rolldown (Rust) — same bundler, same output │
│ │
│ Benefits: │
│ ✅ Dev and prod behavior identical │
│ ✅ One plugin to rule both │
│ ✅ Rust speed for production builds │
│ ✅ Existing Rollup plugins still work │
└────────────────────────────────────────────────────────┘
SWC — Speedy Web Compiler Rust
SWC is a Rust-based JavaScript/TypeScript compiler. Its job: take .ts / .tsx source
and output JavaScript — the same job Babel did, but 17× faster. It does not type-check
(that's still tsc's job).
SWC vs Babel
// .babelrc
{
"presets": [
"@babel/preset-typescript",
"@babel/preset-react",
["@babel/preset-env", {
"targets": "defaults"
}]
]
}
// Speed: ~2s for 1000 files
// Plugins: huge ecosystem
// Type checking: ❌ separate tsc step
// .swcrc
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": { "runtime": "automatic" }
},
"target": "es2022"
}
}
// Speed: ~0.1s for 1000 files (17x faster)
// Type checking: ❌ still need tsc --noEmit
Where SWC is used
- Next.js: Replaces Babel since Next.js 12. You don't configure it — Next does automatically.
- Parcel 2: Uses SWC as its transformer.
- Jest / Vitest:
@swc/jesttransforms test files faster than Babel-jest. - SWC CLI: Use standalone to transpile TS→JS scripts.
# Install
npm install -D @swc/core @swc/jest
# jest.config.ts
export default {
transform: {
'^.+\\.(t|j)sx?$': ['@swc/jest']
}
}
# Result: Jest startup 3-5× faster
Turbopack Rust
Turbopack is Vercel's Rust-based incremental bundler, built by Tobias Koppers (webpack's creator) and the Next.js team. It powers the Next.js dev server. Unlike Rolldown (which replaces Rollup), Turbopack is designed to replace webpack.
The key word: incremental
Turbopack's architecture is fundamentally different from traditional bundlers. It uses a demand-driven computation graph — it only computes what you asked for and caches everything aggressively.
# Dev server with Turbopack (stable in Next.js 15)
next dev --turbopack
# or in package.json:
{ "dev": "next dev --turbopack" }
# next.config.ts
const nextConfig = {
experimental: {
turbopack: {
rules: {
// custom rules
}
}
}
}
# Speed on large apps:
# Cold start: ~700ms vs webpack's ~8s
# HMR update: ~10ms vs webpack's ~200ms
Turbopack vs Rolldown
| Turbopack | Rolldown | |
|---|---|---|
| Made by | Vercel | VoidZero (Evan You) |
| Replaces | webpack (dev) | Rollup + esbuild |
| Used in | Next.js | Vite 6 |
| Plugin API | Custom (webpack-inspired) | Rollup-compatible |
| Production builds | Not yet (planned) | Yes |
| Incremental | Yes (core design) | Partial |
| Framework | Next.js only | Vite ecosystem |
Biome Rust
Biome (formerly Rome) is an all-in-one Rust tool that replaces Prettier (formatter) and ESLint (linter) with a single binary. 97% Prettier-compatible formatting, 25× faster.
Biome vs Prettier + ESLint
# 3 tools, 3 configs, 3 package installs
npm install -D \
prettier \
eslint \
@typescript-eslint/eslint-plugin \
@typescript-eslint/parser \
eslint-config-prettier # prevent conflicts!
# .eslintrc.json
# .prettierrc
# .eslintignore
# .prettierignore
# Scripts in package.json for each
# 1 tool, 1 config
npm install -D @biomejs/biome
# biome.json
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": { "recommended": true }
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
}
}
# Format all files
npx biome format --write .
# Lint all files
npx biome lint .
# Format + lint + fix in one pass
npx biome check --write .
# CI (fail on issues, don't fix)
npx biome ci .
# VS Code: install "Biome" extension and set as default formatter
Biome vs Oxlint
| Biome | Oxlint | |
|---|---|---|
| Formatting | Yes (replaces Prettier) | No |
| Linting | Yes (replaces ESLint) | Yes |
| ESLint plugin compat | No | Partial |
| Import sorting | Yes | No |
| Config complexity | Simple (one file) | Simple |
| Use with ESLint | Can coexist | Designed to complement |
| Best for | Replace Prettier + ESLint entirely | Speed up ESLint-heavy setups |
esbuild Go — The Pioneer
esbuild isn't Rust, but it started the native-speed tooling revolution. Written in Go by Evan Wallace, it proved that JS tooling could be 10–100× faster. Vite currently uses it for dep pre-bundling and TypeScript transpilation.
What esbuild does
- Bundles JS/TS (fast, but less flexible than Rollup — no circular dep analysis, simpler tree-shaking)
- Transpiles TypeScript → JavaScript (strips types only, no type checking)
- Minifies JS/CSS
- Used by: Vite (internally), many CLIs for fast builds
esbuild's limits (why Rolldown still matters)
- Limited plugin API — can't replicate the full Rollup plugin ecosystem
- No advanced code splitting strategies
- No CSS Modules support natively
- No
import.metaawareness at the level Rollup has - Hard to extend for complex use cases
Speed Benchmarks
Relative speeds across key operations (all numbers approximate, hardware-dependent).
Parser throughput (lines/second)
Linting (1000 files)
Format 500 files
What Replaces What
| Old tool | Language | New tool | Language | Used in | Action needed |
|---|---|---|---|---|---|
| Babel | JS | SWC | Rust | Next.js (auto), Jest | None for Next.js. Update Jest config. |
| Rollup | JS | Rolldown | Rust | Vite 6 | Update Vite. Plugins keep working. |
| esbuild (in Vite) | Go | Rolldown | Rust | Vite 6 | None — Vite handles it. |
| webpack | JS | Turbopack | Rust | Next.js --turbopack | Add --turbopack flag. |
| Prettier | JS | Biome | Rust | Any project | Install Biome, delete Prettier config. |
| ESLint | JS | Oxlint + Biome | Rust | Any project | Gradual: add Oxlint alongside ESLint first. |
| ts-node | JS | tsx / bun | Rust/Zig | Scripts, CLI | Replace ts-node script.ts with bun script.ts. |
What You Actually Need to Know
Most of this is infrastructure. You don't configure OXC, Rolldown, or Turbopack directly. Here's what actually matters for your day-to-day work.
If you use Next.js
- SWC is already active — Babel is gone. You don't need to do anything.
- Run
next dev --turbopackfor a faster dev server (stable in Next.js 15). - Some Babel plugins don't have SWC equivalents — check your config if you use unusual Babel transforms.
If you use Vite
- Vite 6 brings Rolldown — update and your builds get faster automatically.
- Your existing Vite/Rollup plugins keep working (Rolldown is Rollup-compatible).
- Dev/prod parity improves — fewer "works in dev but not in prod" bugs.
Things to actively adopt today
- Biome: Drop Prettier + ESLint for new projects. One config, much faster.
- Oxlint: Add to existing projects alongside ESLint for fast basic checks.
- bun: Use as your package manager and for running TS scripts directly.
- Enable
--turbopackin Next.js (Next.js 15, one flag) - Add Biome to new projects (replaces Prettier + ESLint)
- Use
bunfor installs and TS scripts - Add Oxlint to existing projects for faster lint CI step
- Update Vite → get Rolldown for free
Setting Up Each Tool
Biome — replace Prettier + ESLint
# Install
npm install -D @biomejs/biome
# Initialize config
npx biome init
# Migrate from Prettier (imports settings)
npx biome migrate prettier --write
npx biome migrate eslint --write
# package.json scripts
{
"scripts": {
"format": "biome format --write .",
"lint": "biome lint .",
"check": "biome check --write .",
"ci": "biome ci ."
}
}
# VS Code: install "Biome" extension
# Set as default formatter: Cmd+Shift+P → Preferences: Default Formatter → Biome
Oxlint — alongside ESLint
# Install
npm install -D oxlint
# package.json — run oxlint first (fast), then ESLint for complex rules
{
"scripts": {
"lint": "oxlint . && eslint ."
}
}
# To avoid duplicate rules, disable ESLint rules that oxlint covers:
# eslint-plugin-oxlint does this automatically
npm install -D eslint-plugin-oxlint
SWC with Jest
# Install
npm install -D @swc/core @swc/jest
# jest.config.ts
export default {
transform: {
'^.+\\.(t|j)sx?$': ['@swc/jest', {
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'automatic' } }
}
}]
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx']
};
Decision Guide
| Goal | Tool | Effort |
|---|---|---|
| Faster Next.js dev server | next dev --turbopack | One flag |
| Faster Vite builds | Update to Vite 6 | npm update vite |
| Replace Babel in Jest | @swc/jest | Low — update jest.config |
| Replace Prettier | Biome | Low — one config file |
| Replace Prettier + ESLint | Biome | Medium — migrate rules |
| Speed up ESLint in CI | Oxlint | Low — add one script |
| Run TypeScript scripts | bun or tsx | Install and use |
| New project from scratch | Vite + Biome + pnpm + bun | Low — all work together |