React and Next.js
The React entry point wraps the native element in a small, strict adapter. It uses React props for configuration, registers Orbz after the component mounts, and keeps framework-specific code out of the native component.
Render Orbz
import { Orbz } from "@neongate-ai/orbz/react";
export function AssistantOrb() {
return (
<Orbz
state="idle"
size={300}
speed={1}
preset="neongate"
reducedMotion="system"
elevated
/>
);
}Numbers passed to size become pixels; strings such as "18rem" are kept as
CSS lengths. Boolean false values remove their corresponding native
attribute.
Connect React state
Use props rather than reaching into the custom element:
import { Orbz, type OrbzState } from "@neongate-ai/orbz/react";
import { useState } from "react";
export function VoiceSession() {
const [state, setState] = useState<OrbzState>("idle");
const [paused, setPaused] = useState(false);
return (
<section>
<div role="status" aria-live="polite">
<Orbz
state={state}
preset="neongate"
size="min(20rem, 72vw)"
paused={paused}
elevated
/>
<p>Assistant is {state}</p>
</div>
<button type="button" onClick={() => setState("listening")}>
Listen
</button>
<button type="button" onClick={() => setState("thinking")}>
Think
</button>
<button type="button" onClick={() => setState("speaking")}>
Speak
</button>
<button type="button" onClick={() => setPaused((value) => !value)}>
{paused ? "Resume motion" : "Pause motion"}
</button>
</section>
);
}Use it in Next.js
Put interactive Orbz code in a Client Component. Keep data loading and the rest of the page in Server Components as usual.
"use client";
import { Orbz, type OrbzState } from "@neongate-ai/orbz/react";
import { useState } from "react";
export function AssistantOrb() {
const [state, setState] = useState<OrbzState>("idle");
return (
<div>
<div role="status" aria-live="polite">
<Orbz state={state} preset="neongate" size={320} elevated />
<p>Assistant is {state}</p>
</div>
<button type="button" onClick={() => setState("listening")}>
Start session
</button>
</div>
);
}import { AssistantOrb } from "./assistant-orb";
export default function AssistantPage() {
return (
<main>
<h1>Voice assistant</h1>
<AssistantOrb />
</main>
);
}No dynamic(..., { ssr: false }) workaround is required for the package
itself. The entry points do not evaluate an HTMLElement subclass at module
scope, and registration becomes a safe no-op when a DOM is unavailable. Keep
the initial props deterministic between the server response and the first
client render, then update them after mount.
Presets and custom colors are exclusive
Preset mode:
<Orbz state="idle" preset="peach" />Custom palette mode:
<Orbz
state="idle"
colorPrimary="#7C3AED"
colorSecondary="#22D3EE"
colorAccent="#F472B6"
colorHighlight="#FDE68A"
colorBackground="#09090B"
/>OrbzProps is a TypeScript union: setting preset makes every custom color
prop never, while using a custom color makes preset unavailable. If
untyped runtime data bypasses that check and sends both, the native component
applies the preset and logs an error.
The adapter is intentionally strict
The accepted props are state, size, speed, paused, elevated,
reducedMotion, preset, and the five camel-cased color props. Arbitrary host
props, className, style, children, and forwarded refs are outside the
public contract.
This means React state should control the orb. The native play(), pause(),
and restart() methods are not surfaced by the adapter because it deliberately
does not expose a ref escape hatch.
// Prefer declarative props.
<Orbz state={isPlaying ? "speaking" : "idle"} paused={!isPlaying} />See React API for the complete prop contract and the Next.js example for a full application.