No rules of hooks
No dependency arrays and no rules of hooks. The compiler tracks what your effects, memos and callbacks actually use, and hooks can sit behind conditions or early returns.
The successor to Inferno, carrying its performance-first goal forward: React’s hooks, Suspense and actions, compiled ahead of time. No virtual DOM. No rules of hooks. No hand-maintained dependency arrays — the compiler works out what your code captures for you.
// Counter.tsrx — hooks next to output, no rules of hooks
import { useState, useEffect } from 'octane';
export function Counter(props) @{
const [count, setCount] = useState(0);
// A hook behind a condition is fine — slots are
// assigned by call site, not call order.
if (!props.paused) {
useEffect(() => {
console.log('count is now', count);
}); // the compiler infers [count]
}
<button onClick={() => setCount(count + 1)}>{'Count: ' + count}</button>
}// count is now 0No dependency arrays and no rules of hooks. The compiler tracks what your effects, memos and callbacks actually use, and hooks can sit behind conditions or early returns.
Independent use() calls start together instead of suspending one at a time down the tree. Nested fetches warm up early, and streaming SSR sends each boundary as soon as it’s ready.
Templates compile to cloned nodes and direct DOM writes, and keyed @for lists move the fewest nodes possible. Plain .tsx still works, so you can adopt .tsrx one component at a time.
Hooks, memo, context, portals, transitions, actions, controlled forms and Suspense all behave the way you expect. Events are native, refs are just props, and 52 first-party bindings cover the libraries you already use.
Octane components are the components you already write. They are plain functions with hooks, props, and context. If you know React, you know Octane. Your instincts about where state lives and how data flows carry over on day one.
You don’t have to rewrite your app to adopt it. Keep your existing TSX, move components to TSRX one at a time, and ship the whole way. Components keep their shape along the way, so AI agents can migrate an app too, without redesigning it around a new reactive model.
Signals are a good tool, and you can still use them in an Octane app when they fit. But a framework built on signals makes every app represent and read state the signals way. Octane keeps components as plain functions that read from top to bottom. The compiler does the extra work, so your code doesn’t have to.
The trade holds up. Signals still win the workloads built for them. Across the checked-in suites below, Octane keeps pace without asking you to rewire how you think. See what TSRX adds →
Octane compiles ahead of time, so a misconfiguration usually degrades instead of erroring. Two copies of the runtime in one tree break hooks and context silently, because hook state is keyed per runtime instance. Plain tsc mis-handles every .tsrx file. A declare module '*.tsrx' shim turns your own components into any. octane doctor is 20 checks over exactly those.
pnpm add -D @octanejs/clioctane initWire Octane into a project you already have.octane doctor --fixFind the quiet breakage, then repair it.octane add react-hook-formInstall the binding that ports it.octane explain 3Decode a minified production error.octane mcp addGive your coding agent the Octane tools.Already have a React 19 app? Drop compiled Octane islands into it with a single component. Events stay native and delegated, islands read your real React context with plain use(), and they render on the server and hydrate on the client. Migrate one component at a time — no rewrite, no big bang.
The one thing that doesn’t cross over: React Server Components. Everything else — hooks, Suspense, context, SSR — comes along.
Read the OctaneCompat guide →// App.tsx — your existing React 19 app
import { OctaneCompat } from 'octane/react';
import { Counter } from './islands/Counter.tsrx';
export function App() {
return (
<div className="dashboard">
<h1>Ported one component at a time</h1>
{/* A compiled Octane island, hosted inside React with typed
props. Native events, real React context, SSR + hydration. */}
<OctaneCompat>
<Counter start={3} />
</OctaneCompat>
</div>
);
}Octane supports Three.js through @octanejs/three – a technical-preview port of React Three Fiber.
Browse all the bindings →Pick the frameworks and suite for the bar chart, or scan the whole grid — every cell is a geometric mean of that suite’s per-operation scores relative to Octane. Lower is better.
| suite | Octane (.tsrx) | React 19 | Preact 10 | Solid 2.0 beta | Svelte 5 | Ripple 0.3 | Vue Vapor 3.6 beta |
|---|---|---|---|---|---|---|---|
| js-framework | 1× | 2.5× | 2.2× | 1.1× | 1.5× | 1.0× | 1.0× |
| todomvc | 1× | 2.4× | 2.4× | 1.2× | 1.2× | 0.78× | 0.91× |
| chat-stream | 1× | 3.8× | 2.7× | 1.4× | 2.0× | 1.2× | 1.1× |
| js-framework-reorder | 1× | 2.9× | 6.4× | 1.5× | 2.2× | 1.6× | 2.2× |
| dbmon | 1× | 1.8× | 2.0× | 2.8× | 1.2× | 1.2× | 1.1× |
| effectful-list | 1× | 2.3× | 3.6× | 0.59× | 0.74× | 1.0× | 0.66× |
| memo-wall | 1× | 6.1× | 7.5× | 0.39× | 1.2× | 2.4× | 0.35× |
| recursive-context | 1× | 1.3× | 1.0× | 1.0× | 1.7× | 0.82× | 0.90× |
| signal-favoring | 1× | 5.9× | 3.2× | 0.66× | 0.78× | 0.29× | 0.31× |
| portal-swarm | 1× | 7.6× | 9.6× | 0.91× | 2.8× | 3.2× | 1.2× |
| async-waterfall | 1× | 11× | 8.7× | 0.90× | 0.90× | 0.88× | — |
| news | 1× | 3.1× | 2.0× | 1.8× | 1.1× | 1.8× | 1.3× |
| streaming-ssr | 1× | 0.99× | 1.1× | 3.3× | — | 0.93× | — |
| bundle-size | 1× | 0.94× | 0.57× | 0.69× | 0.81× | 0.77× | 0.69× |
| ssr-throughput | 1× | 2.4× | 2.3× | 1.6× | 0.97× | 1.4× | 0.86× |
| geomean | 1× | 2.9× | 2.7× | 1.1× | 1.3× | 1.1× | 0.86× |
Each cell is that framework’s score relative to Octane (1×), geometric mean across the suite’s operations. Green is faster, red is slower.