Configs2026年4月14日·1 分钟阅读

Million.js — Make React 70% Faster with a Compiler-Driven Virtual DOM

Million.js compiles React components into an optimized block-based virtual DOM. By analyzing your JSX at build time, it avoids unnecessary diffs and delivers up to 70% faster renders on list-heavy UIs — with zero changes to most components.

Introduction

React's virtual DOM does a lot of work you don't always need. Million.js tackles this with a compiler that replaces hot paths with "blocks" — pre-analyzed VDOM fragments where only dynamic slots are diffed. The result is measurably faster rendering on large lists and tables, with the same React component model.

With over 18,000 GitHub stars, Million.js is used by Wyze, Cal.com, and hundreds of React apps where DOM-heavy UI has become the bottleneck. The block() HOC opts individual components into the faster path.

What Million.js Does

Million.js provides a Babel/SWC/Vite plugin that statically analyzes JSX. Components wrapped in block() are compiled to a Block representation that bypasses most reconciliation — static nodes are hoisted, dynamic slots are tracked individually. Runtime diffing becomes a fast, focused operation.

Architecture Overview

JSX source
    |
[Million Compiler plugin] (Babel/SWC/Vite)
    |
Classifies components:
  +-- wrapped in block(): compiled to Block VDOM
  +-- unwrapped: regular React
    |
Runtime: Million "virtual DOM in a virtual DOM"
  - Static parts skipped
  - Dynamic slots indexed for fast diff
  - Minimizes reconciler work for list items
    |
React commits with much less effort

Self-Hosting & Configuration

// vite.config.ts
import { defineConfig } from "vite";
import million from "million/compiler";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [
    million.vite({ auto: { threshold: 0.05 } }),  // auto-block heuristically
    react(),
  ],
});

// next.config.js
const million = require("million/compiler");
module.exports = million.next({ auto: true })({ /* your next config */ });

Key Features

  • block() HOC — mark hot components for block compilation
  • auto mode — compiler detects list-like components automatically
  • Drop-in — works with existing React codebases
  • Vite/Next/Webpack — plugins for every major bundler
  • For loop<For> component for optimized list rendering
  • Selective adoption — opt-in per component, fallback to React elsewhere
  • TypeScript-first — full types, no @ts-ignore needed
  • Devtools — warnings when blocks can't be optimized

Comparison with Similar Tools

Feature Million.js React Compiler Preact Inferno Solid
React compatible Yes Yes (React 19) Mostly Mostly No (different model)
Adoption cost block() per component Zero (in React 19) Aliased react Similar rewrites Full rewrite
Speed gain 30–70% list-heavy Varies Minor Large Largest
Bundler plugin Required Built-in React 19 None None None
Best For Perf-critical React apps New React codebases Bundle-size reduction Niche New greenfield

FAQ

Q: Is Million.js still relevant with the React Compiler? A: React Compiler (shipping in React 19) focuses on automatic memoization. Million.js targets VDOM reconciliation overhead — different problem. For very large list rendering, Million still offers unique wins.

Q: Will block() break my existing React code? A: Usually not. There are some constraints: no conditional hooks inside blocks, no dynamic element types in hot paths. The compiler warns if a component can't be blockified.

Q: Do I need to wrap every component? A: No — use auto: true to let the compiler pick list components heuristically. Start there and only wrap specific leaf components manually if you need more.

Q: What frameworks does Million.js support? A: React today; a Million core exists for non-React use but the ecosystem is React-focused.

Sources

讨论

登录后参与讨论。
还没有评论,来写第一条吧。

相关资产