Million.js: The O(1) Heist of the Virtual DOM

How a build-time compiler turns React's biggest weakness into a static-speed advantage without a single rewrite.

aidenybai/million

A massive clockwork heart representing a React app, with a small mechanical hand replacing a single gear with a frictionless block gear.
Million.js operates like a precision surgeon, replacing slow rendering paths without stopping the heartbeat of the React app.

Million.js takes a different approach. Instead of replacing React, it augments it with performance optimizations when you need it. Million.js works out of the box, alongside your favorite metaframeworks (Next.js, Remix, etc.) and React libraries.

Aiden Bai, Creator · Unleashing Million.js v2.0.0

Key Takeaways

The O(n) Tax

The Virtual DOM was a brilliant abstraction. It allowed developers to write declarative UI code without worrying about manual browser updates. But this abstraction comes with a hidden cost. Every state change triggers a recursive diffing process. React must compare the new component tree against the old one to find what changed. As applications scale, this O(n) algorithmic complexity becomes a tangible performance bottleneck.

Frameworks like SolidJS and Svelte solved this by abandoning the Virtual DOM entirely. They adopted fine-grained reactivity or compiled imperative updates. But migrating to a new framework is a monumental task. Developers were stuck choosing between the rich React ecosystem and raw performance. Million.js offers a third path.

Anatomy of a Block

Instead of diffing trees, Million.js uses a block architecture. A block is a pre-calculated template with specific holes for dynamic data. When state changes, Million does not search the tree. It maps the new data directly to the holes in an O(1) operation.

Interactive diagram showing the Hole Mapper concept. On the left

Two people painting a mural. One draws every brick by hand, while the other uses a pre-cut metal stencil and spray paint.
Traditional rendering recalculates the entire structure. Block rendering only targets the pre-defined holes.

The Compiler is the Architect

Million.js is fundamentally a compiler. The heavy lifting happens during the build step using a custom Babel plugin. The compiler analyzes your React components, separates the static HTML structure from the dynamic variables, and generates optimized update instructions before the code ever reaches the browser.

Portrait of Aiden Bai, creator of Million.js
import { block } from 'million/react';

const HeavyList = block(({ items }) => (
  <div>
    {items.map(item => (
      <span key={item.id}>{item.value}</span>
    ))}
  </div>
));

The Speed Spectrum

Million sits in a unique position. It provides the ecosystem compatibility of React with the rendering speed of compiled frameworks.

FeatureReactMillion.jsSolidJS
ReconciliationO(n) Tree DiffingO(1) Block UpdatesFine-grained Signals
EcosystemMassiveFull React CompatibilityGrowing Custom Ecosystem
Build Step RequiredNo (JSX only)Yes (Babel Plugin)Yes (Custom Compiler)

When the Magic Fails

Not every component benefits from the block architecture. If a component consists entirely of other React components rather than raw HTML elements, the compiler cannot extract a meaningful static template. The automatic mode uses a heuristic to calculate an improvement score, ensuring it only optimizes code that will actually run faster.

A vintage laboratory scale weighing a heavy nested component against a lightweight static block structure.
The compiler weighs the ratio of static elements to nested components to determine if optimization is worthwhile.