For years, setting up a JavaScript project meant stitching together a patchwork of tools: Vite for dev/build, Vitest for testing, ESLint for linting, Prettier for formatting, maybe Babel or SWC for transforms. Each tool had its own config file, its own plugin ecosystem, its own version matrix to manage. It worked, but it was fragile.
VoidZero — the company Evan You founded to build unified JavaScript tooling — shipped Vite+ in early 2026. It replaces that entire patchwork with a single CLI powered by a shared Rust core. This is not an incremental improvement. It is a fundamental rethinking of the frontend toolchain.
Let me walk through what Vite+ actually is, why the performance numbers are staggering, and how to migrate an existing project.
The Fragmentation Problem
A typical React or Vue project in 2025 had a config surface that looked something like this:
project/
├── vite.config.ts # Build + dev server
├── vitest.config.ts # Test runner
├── eslint.config.js # Linting rules
├── .prettierrc # Formatting rules
├── .prettierignore # Formatting exclusions
├── tsconfig.json # TypeScript
├── tsconfig.node.json # TypeScript (node context)
├── postcss.config.js # CSS processing
└── babel.config.js # Transforms (maybe)That is 8-9 config files before you write a single line of application code. Each tool parses your source files independently. ESLint builds its own AST. Prettier builds its own AST. Vite builds its own module graph. Vitest builds yet another. You are paying the cost of parsing your codebase four or five times over.
Beyond performance, there is a coordination problem. ESLint and Prettier famously conflict — you need eslint-config-prettier to disable formatting rules in ESLint, then run Prettier separately. Vitest needs to align with your Vite config but uses a separate file. Plugin authors maintain parallel implementations for each tool.
The Real Cost
| Task | Tool | Parses Source? | Has Own Plugin System? |
|---|---|---|---|
| Dev server | Vite | Yes | Yes |
| Production build | Vite + Rollup | Yes | Yes (different from dev) |
| Testing | Vitest | Yes | Yes |
| Linting | ESLint | Yes | Yes |
| Formatting | Prettier | Yes | Yes |
| Type checking | TypeScript | Yes | N/A |
Six tools, six separate AST parses, four separate plugin ecosystems. Every CI run pays this cost. Every developer pays it on every save.
What Vite+ Actually Is
Vite+ is a single CLI that wraps five tools under a unified interface:
┌─────────────────────────────────────────────────┐
│ vite+ │
│ │
│ ┌───────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Vite 8 │ │ Vitest │ │ Rolldown │ │
│ │ Dev/Build │ │ Testing │ │ Bundler │ │
│ └───────────┘ └──────────┘ └──────────────┘ │
│ │
│ ┌───────────┐ ┌──────────┐ │
│ │ Oxlint │ │ Oxfmt │ │
│ │ Linting │ │ Formatting│ │
│ └───────────┘ └──────────┘ │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Shared Rust Core (Oxc Parser) │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘The critical insight is the shared Rust core. Oxlint, Oxfmt, and Rolldown all use the Oxc parser. Your source code is parsed once into a single AST, then linting, formatting, and bundling all operate on that same representation. This is not just a convenience wrapper — it is an architectural unification.
The Components
Vite 8 — The dev server and build orchestrator you already know, now using Rolldown as its default bundler instead of Rollup (dev) and esbuild (production).
Rolldown — A Rust-based bundler that is API-compatible with Rollup. It replaces both esbuild (used for dependency pre-bundling) and Rollup (used for production builds) in Vite. One bundler, both environments.
Vitest — The test runner, unchanged in API but now sharing the same module graph and transform pipeline as the dev server. No more config drift between vite.config.ts and vitest.config.ts.
Oxlint — A Rust-based linter that reimplements the most-used ESLint rules. It is not a wrapper around ESLint — it is a ground-up rewrite using the Oxc parser.
Oxfmt — A Rust-based formatter in the style of Prettier, but 30x+ faster. It aims for high Prettier compatibility while being opinionated about defaults.
Performance Benchmarks
The numbers are not incremental improvements. They are order-of-magnitude jumps.
Linting: Oxlint vs ESLint
Benchmarked on a 2,400-file TypeScript React monorepo (M2 MacBook Pro):
| Metric | ESLint 9 | Oxlint | Speedup |
|---|---|---|---|
| Cold run | 48.2s | 0.6s | 80x |
| Warm run (cached) | 12.1s | 0.5s | 24x |
| Memory usage | 1.2 GB | 89 MB | 13x less |
| Config files needed | 1-3 | 0-1 | — |
Oxlint currently covers roughly 500+ rules — the ones that matter for the vast majority of projects. It does not cover every niche ESLint plugin, but it handles eslint:recommended, typescript-eslint, eslint-plugin-react, eslint-plugin-import, and eslint-plugin-unicorn rules that teams actually enable.
Formatting: Oxfmt vs Prettier
Same codebase:
| Metric | Prettier 3 | Oxfmt | Speedup |
|---|---|---|---|
| Full format | 14.8s | 0.42s | 35x |
| Check mode | 11.3s | 0.31s | 36x |
| Single file | 85ms | 3ms | 28x |
Oxfmt's output is not identical to Prettier in every edge case, but the compatibility rate is above 97% for typical TypeScript/JSX codebases. The remaining differences are mostly around line-break decisions in deeply nested ternaries and some decorator formatting.
Building: Rolldown vs Rollup + esbuild
Production build of a medium Next.js-scale app (800 modules, code splitting enabled):
| Metric | Rollup + esbuild | Rolldown | Speedup |
|---|---|---|---|
| Production build | 22.4s | 3.8s | 5.9x |
| Dev cold start | 1.8s | 0.9s | 2x |
| HMR update | 120ms | 45ms | 2.7x |
| Bundle size | 1.42 MB | 1.39 MB | ~same |
Rolldown produces comparable or slightly smaller bundles thanks to better tree-shaking in its unified pipeline. The big win is build speed — nearly 6x faster production builds fundamentally change the CI feedback loop.
Hands-On Migration Guide
Step 1: Install Vite+
# Remove old tools
pnpm remove eslint prettier eslint-config-prettier \
eslint-plugin-react @typescript-eslint/eslint-plugin \
@typescript-eslint/parser
# Install Vite+
pnpm add -D vite-plusStep 2: Replace Config Files
Before migration, you might have:
eslint.config.js
.prettierrc
.prettierignore
vite.config.ts
vitest.config.tsAfter migration, you have one file:
// vite-plus.config.ts
import { defineConfig } from 'vite-plus'
export default defineConfig({
// Vite config (same API as before)
server: {
port: 3000,
},
build: {
target: 'es2022',
},
// Test config (same as Vitest API)
test: {
environment: 'jsdom',
include: ['src/**/*.test.{ts,tsx}'],
coverage: {
provider: 'v8',
thresholds: { lines: 80 },
},
},
// Lint config
lint: {
rules: {
'no-console': 'warn',
'no-debugger': 'error',
'react/no-array-index-key': 'warn',
'typescript/no-explicit-any': 'warn',
},
ignore: ['dist/', 'node_modules/'],
},
// Format config
format: {
printWidth: 100,
singleQuote: true,
trailingComma: 'all',
},
})Step 3: Update package.json Scripts
{
"scripts": {
"dev": "vite-plus dev",
"build": "vite-plus build",
"test": "vite-plus test",
"lint": "vite-plus lint",
"format": "vite-plus format",
"check": "vite-plus check",
"ci": "vite-plus check && vite-plus test --run && vite-plus build"
}
}The check command runs both linting and format checking in a single pass — one AST parse, two operations. The ci script chains everything together and is significantly faster than the equivalent multi-tool pipeline.
Step 4: Update CI
Before:
# .github/workflows/ci.yml
jobs:
check:
steps:
- run: pnpm lint # ESLint (~48s)
- run: pnpm format:check # Prettier (~11s)
- run: pnpm test -- --run # Vitest (~35s)
- run: pnpm build # Vite + Rollup (~22s)
# Total: ~116sAfter:
# .github/workflows/ci.yml
jobs:
check:
steps:
- run: pnpm ci
# check (~1s) + test (~30s) + build (~4s) = ~35sThat is a 3.3x reduction in CI time from tooling alone, not counting the test execution speedup from the shared module graph.
Step 5: Handle ESLint Plugin Gaps
If your project uses niche ESLint plugins that Oxlint does not cover yet, you have two options:
Option A: Run both (transitional)
{
"scripts": {
"lint": "vite-plus lint && eslint --no-eslintrc -c eslint.legacy.js src/",
"lint:fast": "vite-plus lint"
}
}Keep ESLint only for the specific rules Oxlint does not support yet. Run it as a secondary check.
Option B: Write a custom Oxlint rule
Oxlint supports custom rules written in a declarative JSON format for pattern-based checks:
{
"rules": {
"custom/no-barrel-imports": {
"pattern": "import { .* } from '\\./index'",
"message": "Avoid barrel imports for better tree-shaking"
}
}
}For complex rules that need AST traversal, Oxlint provides a Rust plugin API — but that is a bigger investment and only worth it for rules your team uses heavily.
What About Biome?
Biome is the other major player in the "unified Rust tooling" space. It combines linting and formatting (like ESLint + Prettier) but does not include a bundler, dev server, or test runner. Here is how they compare:
| Feature | Vite+ | Biome |
|---|---|---|
| Dev server | Yes (Vite 8) | No |
| Bundler | Yes (Rolldown) | No |
| Test runner | Yes (Vitest) | No |
| Linter | Yes (Oxlint) | Yes |
| Formatter | Yes (Oxfmt) | Yes |
| Single config | Yes | Partial (lint + format only) |
| Rollup plugin compat | Yes | N/A |
Biome is excellent at what it does — linting and formatting. But Vite+ is a broader play: it unifies the entire toolchain, not just the code-quality tools. If you are already using Vite and Vitest, Vite+ is the natural upgrade path.
If you are not using Vite (say, you are in a webpack project), Biome is still a strong choice for the lint/format layer while you plan a broader migration.
Rolldown Deep Dive
Rolldown deserves special attention because it solves the most confusing part of the old Vite architecture.
The Old Problem
Vite used two different bundlers:
- esbuild for dependency pre-bundling (converting
node_modulesto ESM during dev) - Rollup for production builds
This meant dev and production could behave differently. A subtle Rollup plugin might work in production but not in dev, or vice versa. Debugging these inconsistencies was painful.
The Rolldown Solution
Rolldown replaces both. It is a single Rust-based bundler that handles:
- Dependency pre-bundling (replacing esbuild)
- Production builds (replacing Rollup)
- Module resolution and transformation
It maintains API compatibility with Rollup plugins, so most existing plugins work without changes:
// This Rollup plugin works with Rolldown unchanged
import mdx from '@mdx-js/rollup'
export default defineConfig({
plugins: [mdx()],
})The compatibility is not 100% — plugins that depend on Rollup internals or undocumented behavior may need updates. But the public plugin API is faithfully reimplemented.
Bundle Analysis
Rolldown includes built-in bundle analysis that was previously an external plugin:
vite-plus build --analyzeThis generates an interactive treemap showing module sizes, duplicate dependencies, and tree-shaking effectiveness — no need for rollup-plugin-visualizer or webpack-bundle-analyzer.
When to Adopt vs When to Wait
Adopt Now If
- You are starting a new project and want the fastest possible setup
- Your existing project uses Vite + Vitest already (the migration is minimal)
- Your CI pipeline is slow and tooling overhead is a significant portion
- You use a standard ESLint config without many niche plugins
- You want to simplify onboarding (one tool, one config, one mental model)
Wait If
- You depend on niche ESLint plugins that Oxlint does not cover yet (check the Oxlint rules coverage page)
- You have heavy custom ESLint rules written as AST visitors
- Your project uses webpack and you are not ready for a full bundler migration
- You need exact Prettier output compatibility (the 3% difference matters for your team)
- You are in a regulated environment where changing the entire toolchain requires extensive validation
The Pragmatic Middle Ground
You do not have to migrate everything at once. A phased approach works well:
Phase 1: Replace ESLint + Prettier with Oxlint + Oxfmt (biggest time savings)
Phase 2: Upgrade Vite to use Rolldown (better dev/prod consistency)
Phase 3: Unify config into vite-plus.config.ts (simplification)Each phase delivers value independently. You can stop at Phase 1 and still see 80% of the performance benefit.
Setting Up From Scratch
If you are starting a new project, Vite+ has a scaffolding command:
pnpm create vite-plus my-appThe interactive prompts let you pick your framework (React, Vue, Svelte, Solid) and TypeScript preference. The generated project has:
my-app/
├── src/
│ ├── App.tsx
│ ├── main.tsx
│ └── App.test.tsx
├── vite-plus.config.ts # One config file
├── tsconfig.json # TypeScript config
├── package.json # Clean scripts
└── index.htmlTwo config files total (three if you count package.json). Compare that to the 8-9 files we started with.
The generated vite-plus.config.ts includes sensible defaults:
import { defineConfig } from 'vite-plus'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
include: ['src/**/*.test.{ts,tsx}'],
},
lint: {
preset: 'recommended',
},
format: {
preset: 'default',
},
})Run pnpm dev and you have a dev server, test runner, linter, and formatter all working from one config. Run pnpm vite-plus check and your code is linted and format-checked in under a second.
The Bigger Picture
Vite+ is the logical conclusion of a trend that started years ago: JavaScript tooling is being rewritten in faster languages and then unified.
The progression was:
- Individual JS tools (Babel, webpack, ESLint, Prettier) — slow, fragmented
- Faster individual tools (esbuild, SWC, Turbopack) — fast, still fragmented
- Partial unification (Biome for lint+format, Vite for dev+build) — faster, less fragmented
- Full unification (Vite+) — fastest, single toolchain
The key question is not whether this consolidation will happen — it is happening. The question is whether Vite+ becomes the standard or whether competing unified toolchains emerge. Given Vite's existing market share (used by Vue, Nuxt, SvelteKit, Astro, Solid, Remix, and more), Vite+ has a significant distribution advantage.
For most teams, the migration is straightforward, the performance gains are immediate, and the config simplification pays ongoing dividends. If you are setting up a new project in 2026, there is little reason not to start with Vite+. If you are maintaining an existing Vite project, the upgrade path is clear and can be done incrementally.
The era of managing five separate tools to do what one tool can do is ending. That is a good thing.