Octane is a fast JavaScript UI framework, and the successor to Inferno. You write components with the React API you already know, and a compiler turns them into direct DOM code before they ship. No virtual DOM, no rules-of-hooks bookkeeping, and no dependency arrays to maintain by hand.
Created by Dominic Gannaway, who also created Inferno and has worked on React, Lexical, Ripple, and Svelte.
import { useState } from 'octane';
export function Counter() @{
const [count, setCount] = useState(0);
<button onClick={() => setCount(count + 1)}>
{'Count: ' + count}
</button>
}Why Octane
Your React knowledge transfers. useState, useEffect, memo, context,
portals, Suspense, transitions: same API, same mental model, checked case by case
against a large behavioral suite. React-derived coverage is tracked in the
generated parity report rather than inferred
from the size of the suite.
Standard JSX works, .tsrx gives you more. Paste a component from the React
docs into a .tsx file and it runs. Or author in .tsrx, the spiritual
successor to JSX, and get template directives (@if, @for, @switch, @try)
that compile to keyed fast paths, plus an @{ … } shorthand that puts setup next
to the output. Mix both dialects in one app and import across the boundary.
Write the closure, not its dependency list. Omit the array from useEffect,
useMemo, useCallback, and friends, and the compiler derives it from what the
closure actually captures, including stable setters, dispatchers, refs, and state
getters. This is the no-bookkeeping DX people associate with signal frameworks,
without leaving the hooks model. Explicit arrays still mean exactly what they
mean in React.
No rules of hooks. Hooks are tracked by call site, not call order, so a hook
can live inside an if or after an early return. The one rule left is enforced
for you: a hook in a plain JS loop is a compile error, because every iteration
would share a single call-site slot. Use the keyed @for directive instead,
where each item gets its own hook state.
The platform, not a reimplementation of it. Real delegated DOM events,
controlled form components on native events (React's value/checked semantics,
with onInput per edit and native onChange on commit), and refs as plain props
(ref={cb}, ref={obj}, even ref={[a, b]}). No synthetic layer second-guessing
the browser.
No virtual DOM. Components re-render like React, but a compiled render path and an LIS-based keyed reconciler keep the runtime overhead minimal.
Octane is deliberately narrow where React has grown wide: no class components, no Server Components, no synthetic event system. Those are choices, not gaps, and they are written down in Differences from React.
Also in the box
- Editable state that follows its source.
useLinkedStateresets or adjusts local state as soon as an input changes, with no effect and no state update during render. - Promises in render are safe. No
cache()wrapper: creations feedinguse()are memoized at their declarations, including local.thenchains. Independent requests start together, one suspension per stratum, and descendant fetch trees prefetch while an ancestor is still suspended. - Streaming SSR and byte-stable hydration, with out-of-order Suspense flushing over Node or web streams, or buffered and static rendering when you want it.
- Deferred hydration.
<Hydrate>keeps server HTML visible but inert until it is worth activating, and splits its children into their own chunk by default. class/classNamecomposes clsx-style everywhere: strings, arrays, objects, and nesting, at every apply site.- A current-state getter.
useStateanduseReducerreturn[state, update, getState], so a delayed callback can read the latest value instead of a stale capture.
Install
Octane's published packages need Node.js 22 or newer.
pnpm add octane @octanejs/vite-plugin// vite.config.ts
import { defineConfig } from 'vite';
import { octane } from '@octanejs/vite-plugin';
export default defineConfig({
plugins: [octane()],
});// main.ts
import { createRoot } from 'octane';
import { App } from './App.tsrx';
const root = createRoot(document.getElementById('root')!);
root.render(App, { title: 'Hello world!' });To start from nothing instead, scaffold a project that already runs:
npm create octane my-appOr, in a project you already have, let the CLI wire it up, including the
TypeScript settings .tsrx needs:
pnpm dlx @octanejs/cli initRspack and Rsbuild are supported too. Getting started covers all three build tools, server rendering, hydration, streaming, deferred hydration, and profiling.
Status
Octane is in alpha. The runtime, compiler, and SSR/hydration paths all work, but APIs still move.
The core suite contains 3,900+ distinct behavioral tests across conformance,
differential, hydration, runtime, compiler, and SSR coverage. The octane-prod
project reruns the normal suite against the production compiler path, which is
valuable mode coverage but is not counted again as unique tests. This is an
Octane suite count, not a claim that every test was ported from React; the pinned
snapshot and source-attributed React counts live in the
coverage ledger and report.
Documentation
The full docs live at octanejs.dev, a site built with Octane itself. Good places to start:
- Quick start: install, mount, and the
.tsrxessentials. - Build tools: Vite, Rspack, or Rsbuild for SPA compilation and full-stack SSR.
- TSRX vs TSX/JSX: when to reach for each dialect and what TSRX unlocks.
- Differences from React: the deliberate divergences, and why everything else matching React is the point.
- Publishing libraries: package all importable authored code so applications compile libraries against their own Octane runtime.
- Bindings: the
@octanejs/*ports of the React ecosystem. - Framework integrations: use Octane with Astro, Docusaurus, or TanStack Start.
In this repository:
- Getting started: install, build tools, mount, SSR, streaming, deferred hydration, profiling.
- TSRX basics: components, hooks, control flow, class composition, text input events, strong mode.
- Server rendering and deferred hydration: the full references.
- Differences from React: the divergence contract.
- Bindings status: what each
@octanejs/*package ports, its upstream version, and its known divergences.
Packages
This is a pnpm monorepo. docs/packages.md is the
generated inventory; the shape of it is:
octaneis the runtime and the compiler together: rendering, the hook API, the server (SSR) and client (hydration) entry points, and the compiler itself, exposed atoctane/compilerwith bundler adapters atoctane/compiler/viteandoctane/compiler/bundler.- The app layer:
@octanejs/app-coreholds the bundler-neutral config, routing, SSR, hydration codegen, and production handler, and the Vite, Rspack, and Rsbuild integrations build on it.adapter-vercelandadapter-cloudflaredeploy the output;@octanejs/tanstack-startis the TanStack Start integration. - Tooling:
@octanejs/cli(init,doctor,add,explain,mcp add) and@octanejs/mcp-server, which exposes Octane docs and compile tooling to AI agents over MCP. - The
@octanejs/*bindings, each an Octane port of a React library: state (zustand, jotai, valtio, mobx, redux, redux-toolkit, tanstack-store), data and routing (tanstack-query, apollo-client, tanstack-router, remix-router), UI (radix, base-ui, aria, shadcn, motion, dnd-kit, sonner, floating-ui, lucide), forms and content (hook-form, tanstack-form, lexical, tiptap, mdx, i18next), data-heavy screens (tanstack-table, tanstack-virtual, recharts, visx), 3D (three), Web3 (wagmi, rainbowkit), and more.
Parity varies by package. Some are behaviorally complete, others are explicitly
partial or alpha, and
docs/bindings-status.md is the generated table of
record: upstream version, supported surface, known divergences, SSR/hydration
coverage, and when the evidence was last checked.
Contributing
Bug reports, regression tests, docs, bindings, and core fixes are all welcome. CONTRIBUTING.md covers setup, where a change belongs, the test policy, the generated files, and how pull requests are labelled and landed.
pnpm install
pnpm test
pnpm typecheckLicense
MIT