@reactor-team/js-sdk
v2.12.0
Published
Reactor JavaScript frontend SDK — connect React and TypeScript apps to real-time AI video models on Reactor.
Readme
@reactor-team/js-sdk
The official JavaScript/TypeScript SDK for Reactor, the developer platform for real-time world models.
In a few lines of code you can connect a browser or Node app to a Reactor session over WebRTC, render live model video at 30–60 FPS, send typed commands to steer what generates, and receive structured messages back. The SDK ships a React API (ReactorProvider, ReactorView, useReactor, …) for browser apps, and an imperative Reactor class for everything else.
Full reference and guides live at docs.reactor.inc.
Quickstart
The fastest path is create-reactor-app, which scaffolds a Next.js app with auth wired up:
pnpm create reactor-app my-app
cd my-app && pnpm install && pnpm devOr wire it up by hand. Exchange your API key for a short-lived JWT on the server, then mount <ReactorProvider> in the client:
// app/api/token/route.ts
import { NextResponse } from "next/server";
export async function POST() {
const r = await fetch("https://api.reactor.inc/tokens", {
method: "POST",
headers: { "Reactor-API-Key": process.env.REACTOR_API_KEY! },
});
const { jwt } = await r.json();
return NextResponse.json({ jwt });
}// app/page.tsx
"use client";
import { use } from "react";
import { ReactorProvider, ReactorView } from "@reactor-team/js-sdk";
const tokenPromise = fetch("/api/token", { method: "POST" })
.then((r) => r.json())
.then(({ jwt }) => jwt);
export default function App() {
const token = use(tokenPromise);
return (
<ReactorProvider
modelName="your-model-name"
jwtToken={token}
connectOptions={{ autoConnect: true }}
>
<ReactorView className="w-full aspect-video" />
</ReactorProvider>
);
}See the Quickstart and Authentication docs for the full walkthrough, including production auth patterns.
React API
<ReactorProvider> owns the connection. Components, hooks, and the recording surfaces all read state and methods from its context. Don't call connect() unless autoConnect is false; the provider can manage it for you.
Display video
<ReactorView> binds to the model's main_video track and manages the underlying <video> element. Most models expose a single video output and you can drop it in unchanged:
<ReactorView className="w-full aspect-video" videoObjectFit="cover" />To play model audio alongside the video, set audioTrack to the track name declared by the model. To attach the stream to your own <video> element instead, read tracks[name] from useReactor().
Send commands
Commands are the model's typed RPC surface (set_prompt, set_image, and any custom events declared in the model schema). The full catalog is in the Model API Reference.
import { useReactor } from "@reactor-team/js-sdk";
function PromptInput() {
const { status, sendCommand } = useReactor((s) => ({
status: s.status,
sendCommand: s.sendCommand,
}));
return (
<button
disabled={status !== "ready"}
onClick={() => sendCommand("set_prompt", { prompt: "a forest at dawn" })}
>
Set prompt
</button>
);
}Publish webcam input
For video-to-video models, drop <WebcamStream> inside the provider and name the track the model expects. The component handles getUserMedia, lifecycle, and cleanup:
<ReactorProvider modelName="your-model-name" jwtToken={token}>
<ReactorView className="w-full aspect-video" />
<WebcamStream track="webcam" className="w-48 aspect-video" />
</ReactorProvider>Receive messages
Models emit structured messages back to your app, such as progress updates, state snapshots, or custom model events. Subscribe with useReactorMessage. Select store fields one at a time so a component only re-renders when something it actually uses changes — passing (s) => s will rerender on every track frame, status flip, and error:
import { useReactorMessage } from "@reactor-team/js-sdk";
function FrameCounter() {
const [frame, setFrame] = useState(0);
useReactorMessage((msg) => {
if (msg.type === "state") setFrame(msg.data.current_frame);
});
return <div>Frame: {frame}</div>;
}Error handling
useReactor((s) => s.lastError) exposes the most recent ReactorError. Recoverable errors can be retried via s.reconnect(). Full code catalog: ReactorError.
Upload files
Bind an <input type="file"> to the model with uploadFile and then pass the returned FileRef into any command:
const { uploadFile, sendCommand } = useReactor((s) => ({
uploadFile: s.uploadFile,
sendCommand: s.sendCommand,
}));
const ref = await uploadFile(file);
await sendCommand("set_image", { image: ref });Imperative API
For non-React apps such as Electron shells, game engines, or Node scripts, use the Reactor class directly. It exposes the same surface as the React store, without the React glue:
import { Reactor } from "@reactor-team/js-sdk";
const reactor = new Reactor({
apiUrl: "https://api.reactor.inc",
modelName: "your-model-name",
});
reactor.on("statusChanged", (status) => console.log("status:", status));
reactor.on("message", (msg) => console.log("model:", msg));
reactor.on("trackReceived", (name, _track, stream) => {
if (name === "main_video") videoEl.srcObject = stream;
});
await reactor.connect(await fetchJwt());
await reactor.sendCommand("set_prompt", { prompt: "a forest at dawn" });Recording
Sessions are recorded as they stream. Ask the runtime for the last N seconds with requestClip, or the full session so far with requestRecording. Both resolve to a Clip value that you can pass to <ClipPlayer> for preview, or to <ClipDownloadButton> to save as MP4.
import { ClipPlayer, ClipDownloadButton } from "@reactor-team/js-sdk";
import type { Clip } from "@reactor-team/js-sdk";
function ClipModal({ clip, jwt }: { clip: Clip; jwt: string }) {
return (
<>
<ClipPlayer clip={clip} getJwt={() => jwt} />
<ClipDownloadButton clip={clip} getJwt={() => jwt} />
</>
);
}Capture a clip from anywhere inside the provider:
const reactor = useReactor((s) => s.internal.reactor);
const clip = await reactor.requestClip(10); // last 10 s<ClipPlayer> uses native HLS on Safari/iOS; install hls.js to support Chrome, Firefox, and Edge:
pnpm add hls.jsDownloads are remuxed into a flat MP4 (start_time=0, faststart, major_brand=isom) under the hood so the resulting file uploads cleanly to Twitter, Instagram, TikTok, and YouTube without any extra setup on your side — the H.264 / AAC bitstream itself is passed through untouched.
Full walkthrough, error codes, and the headless useClipDownload hook: Recordings.
Typed model SDKs
For models with a published typed SDK, prefer @reactor-models/<name>. It re-exports everything here and adds typed commands, messages, and hooks for one specific model. Use this base SDK when the model doesn't have a typed package yet, or when you want to stay model-agnostic.
API surface
| Surface | Where it lives |
| -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Reactor | Reactor class |
| <ReactorProvider>, <ReactorView>, <WebcamStream>, <ReactorController> | React components |
| useReactor, useReactorMessage, useStats | React hooks |
| <ClipPlayer>, <ClipDownloadButton>, useClipDownload, RecordingClient, RecordingError, fetchPlaylist, parsePlaylist | Recordings |
License
Apache 2.0 © 2024-2026 Reactor Technologies, Inc.
