Octane compiles the virtual DOM away. Do you need it to?


A couple of weeks ago a project called Octane hit the front page of Hacker News: 127 points, a title promising React's programming model with the virtual DOM compiled out. My first reaction was the one I have to most "we rebuilt React, but faster" posts, which is to keep scrolling. Then I saw who wrote it.

Octane is by Dominic Gannaway. He created Inferno, he was on the React core team, he maintained Svelte for a while, and he wrote Lexical, which happens to be the rich-text editor running the very field I'm typing this into. That combination is unusual. This is not someone who discovered the VDOM has overhead last Tuesday. So the interesting question stopped being "is this cool" (it is) and became the one I actually have to answer as someone shipping a small site: who needs this, and for how long?

What Octane actually changes

Three things, and they build on each other.

The first is the headline: no virtual DOM. Instead of building a tree of elements every render and diffing it against the last one, Octane compiles your components ahead of time into code that updates the real DOM directly. Lists get a keyed reconciler that moves only the nodes that have to move. It drops React's synthetic event system for real delegated DOM events. The reconciliation work that React does at runtime, Octane mostly does at build time.

The second is the dependency array, and this is the part that will get people's attention. Octane reads what your useEffect or useMemo closure actually references and derives the dependency list for you. You stop writing the array. If you've ever shipped a stale-closure bug because you forgot to list something, you already understand the appeal.

The third is the one that made me raise an eyebrow: the Rules of Hooks are gone. Because reactivity is derived from what the code captures rather than from the order hooks are called in, you can call a hook inside a conditional or after an early return. The homepage example is a counter with a hook tucked inside an if, which is the kind of thing that has been a lint error for the entire life of hooks.

function Counter({ enabled }) {
  if (enabled) {
    const [count, setCount] = useState(0);
    return <button onClick={() => setCount(count + 1)}>{count}</button>;
  }
  return <span>disabled</span>;
}

The instinct here is the same one behind React's own compiler: stop asking the developer to hand-annotate reactivity, and figure it out at build time instead. Octane just pushes the idea a lot further. There's also a new file format, .tsrx, with template directives like @for and @if that give the compiler stronger guarantees than a plain .map() can, so it can emit faster loop code. The format is optional. Plain .tsx works, and you can mix the two in one app, which is the basis for the "migrate one component at a time" story.

Where this is genuinely worth it

There's a real category of app where the virtual DOM is the bottleneck, and for those teams Octane is not a curiosity.

Think about the workloads where re-render cost is the thing you fight: a trading dashboard repainting on every tick, a live chart, a virtualized grid with tens of thousands of rows, a canvas-heavy data-viz tool, an animation-dense interface, anything targeting low-end devices where the CPU budget is tiny. In those apps the diffing and the reconciliation memory churn are measurable, and shaving them is worth real engineering effort. People in that corner already reach for tricks like aggressive memoization, windowing, and dropping out of React entirely for the hot path. A compiler that removes the overhead at the source is a better answer than any of those workarounds.

For those teams the ergonomic wins are a bonus. They didn't come for the missing dependency array. They came because a frame budget is 16 milliseconds and they keep blowing it.

Why most of us don't need it

Here's the part I have to be honest about, because it applies to me and probably to you.

The overwhelming majority of React apps are CRUD forms, dashboards nobody refreshes 60 times a second, marketing sites, and internal tools. This blog is a Payload CMS front end. For that entire category, the virtual DOM was never the thing making anything slow. What actually makes these apps feel slow is network latency, images that were never resized, a JavaScript bundle three times bigger than it needs to be, and request waterfalls where the browser learns what to fetch one round trip at a time.

None of those get better because you compiled the VDOM away. You can delete every millisecond of diffing from a page and the user still waits on the 400 KB hero image and the API call that couldn't start until three other calls finished. Optimizing render performance in an app whose bottleneck is the network is like tuning the engine on a car stuck in traffic.

And adopting Octane is not free. It's a new compiler, a new file format if you want the fast paths, and a new set of failure modes to learn. What breaks when the Rules of Hooks disappear? I don't fully know yet, and "I don't fully know yet" is exactly the cost. For a team shipping a perfectly normal product, that cost buys performance the users will never perceive. That's a bad trade, and it stays a bad trade no matter how good the compiler is.

The one part of the pitch that does land for everyone is the missing dependency array. That's a genuine developer-experience win at any scale. But it's worth asking whether you need a new framework to get it.

The migration story is where I'd push hardest

The incremental-adoption claim is the one I'd want to sit with before believing. Mix .tsx and .tsrx, convert a component at a time, leave the rest of the app alone. There's a compatibility layer, OctaneCompat, for dropping Octane components into an existing React 19 app.

On paper that's the right design. In practice, the interesting question is the boundary. When an Octane component that can call hooks conditionally renders a normal React component that can't, and vice versa, what exactly are the rules at that seam? What happens to context that crosses it? How do the two reconcilers behave when they're interleaved in one tree? Those are the questions that decide whether "incremental" means "genuinely low-risk" or "fine until the first weird bug that costs you a day." I haven't run a mixed tree through it, so I'm not going to pretend I know. If you're evaluating Octane seriously, that seam is where I'd spend the first afternoon, not the greenfield demo.

It's also fair to note the project is honest about its stage. The README says alpha: the runtime, compiler, and SSR paths work, but the APIs still move. The marketing site reads more finished than that, and the Hacker News thread gave the copy a hard time for sounding machine-written, which the author took in stride. So the honest summary is a credible author doing real engineering on something that's still genuinely early, and none of those cancels out the others.

What React is doing while Octane ships

This is the part that makes me think Octane is less a competitor to React than a preview of where React is already heading.

The React Compiler went stable last year. It does automatic memoization: it analyzes your components at build time and inserts the equivalent of useMemo, useCallback, and React.memo for you, so you can mostly stop writing them by hand. It's production-ready and battle-tested at Meta. The one catch is that it's still opt-in, a build flag rather than the default, so a lot of teams haven't turned it on yet.

Notice what that does to Octane's most broadly appealing selling point. The biggest ergonomic pain for everyday React isn't the virtual DOM, which most people never think about. It's the manual memoization dance, and React is handing you the fix for that inside idiomatic React, with no new file format and no new mental model. The React Compiler doesn't go as far as Octane. It won't remove your useEffect dependency arrays and it won't let you call hooks in a conditional. But it captures the part of the win that most teams will actually feel.

And Octane isn't operating in a vacuum. The whole ecosystem has been moving the same direction for years. Solid and Svelte have been compiling fine-grained reactivity for a while, Svelte 5 leaned all the way into signals, and Vue's Vapor mode compiles away its own virtual DOM. In Octane's own benchmark table, Vapor sits right next to it. The industry has largely agreed that the compiler should figure out reactivity so humans stop hand-managing it. The open question is only how much of that React absorbs first-party, and how fast.

So who is it for

If you're in the performance-critical corner, watch Octane closely, and go poke at that migration seam yourself rather than trusting the demo. The author has the track record to make the bet worth taking seriously, and the problem he's solving is real for you in a way it isn't for most people.

If you're building the other 90% of software, the honest answer is that you probably don't need it, and the more likely path is that React quietly hands you most of the upside anyway. Turn on the React Compiler you already have access to and you'll get the ergonomic part of this story today, without adopting a new framework to get it. Octane may well be an early look at where React itself ends up. The bet I'd make is that first-party tooling closes the gap for the mainstream before a separate no-VDOM compiler ever needs to.

Which is a strange thing to say about a genuinely impressive piece of engineering: I'm glad it exists, I'll be watching it, and I hope I never have to reach for it.


Comments

No comments yet. Be the first to comment.

Leave a comment