Skip to Content
Getting startedReact and Next.js

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

assistant-orb.tsx
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.

app/assistant/assistant-orb.tsx
"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> ); }
app/assistant/page.tsx
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.

Last updated on