Quick start

Install Octane, connect it to a page, and build your first stateful component. If you already have a Vite project, the whole path takes three small files.

Install and configure

Install the runtime and the recommended Vite integration:

pnpm add octane @octanejs/vite-plugin

Then add the plugin to Vite:

ts
// vite.config.ts
import { defineConfig } from 'vite';
import { octane } from '@octanejs/vite-plugin';

export default defineConfig({
	plugins: [octane()],
});

The Vite plugin includes the compiler. Use this same integration for client-only SPAs and for apps that add routing, server rendering, hydration, and production server builds.

For Rspack, install @octanejs/rspack-plugin and add new OctaneRspackPlugin() to plugins. For full apps on Rsbuild, install @octanejs/rsbuild-plugin and add pluginOctane() instead.

The build tools guide has complete Vite, Rspack, and Rsbuild examples, including server compilation, routing, SSR, hydration, HMR, production output, and preview.

Build your first component

A component is a TypeScript function that returns UI. This one receives a title, remembers a count, and updates it when the button is clicked:

tsrx
// src/App.tsrx
import { useState } from 'octane';

interface AppProps {
	title: string;
}

export function App(props: AppProps) @{
	const [count, setCount] = useState(0);

	<main>
		<h1>{props.title as string}</h1>
		<p>{'Button pressed ' + count + ' times'}</p>
		<button onClick={() => setCount((current) => current + 1)}>
			Press me
		</button>
	</main>
}

There are only four ideas here: App is the component, props are its inputs, useState is its memory, and onClick handles the browser event. Calling setCount renders the component again with the new value.

The @{ ... } body is TSRX shorthand for returning its final JSX node. If you prefer standard .tsx or .jsx, use a normal function body with return; the APIs and runtime stay the same.

Connect it to the page

Give Octane an empty element in your HTML:

html
<!-- index.html -->
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>

Create a root for that element and render the component into it:

ts
// src/main.ts
import { createRoot } from 'octane';
import { App } from './App.tsrx';

const container = document.getElementById('app')!;
const root = createRoot(container);

root.render(App, { title: 'My first Octane app' });

That is a complete client-rendered Octane app. Start your Vite development server, open the page, and the button will update without replacing the rest of the document.

TSRX at a glance

TSRX keeps rendered branches and lists next to the elements they control. For example, @for renders a keyed list and @empty handles the no-results case:

tsrx
export function TodoList(props) @{
	<ul>
		@for (const todo of props.todos; key todo.id) {
			<li>{todo.label as string}</li>
		} @empty {
			<li>No tasks yet</li>
		}
	</ul>
}

The same family includes @if for two-way branches, @switch for several choices, and @try for loading and error states. Plain JavaScript still belongs in the setup part of the function. The TSRX vs TSX/JSX guide shows when each syntax is the better fit.

Next

  • Build tools — choose Vite, Rspack, or Rsbuild.
  • Core APIs — roots, hooks, boundaries, actions, and SSR.
  • Differences from React — the deliberate divergences (everything else matching React is the point).
  • Bindings — the @octanejs/* ports of the React ecosystem.