@nexbasira/react
React wrapper around @nexbasira/embed.
Two surfaces — a drop-in <NexBasiraSession> component
for the common case, plus a useNexBasiraSession() hook for
custom layouts.
Install
npm install @nexbasira/react @nexbasira/embed react react and @nexbasira/embed are peer
dependencies — they aren't bundled with this package.
Component
import { NexBasiraSession } from "@nexbasira/react";
function InspectionPage({ joinUrl }: { joinUrl: string }) {
return (
<NexBasiraSession
sessionUrl={joinUrl}
height="720px"
onSessionComplete={(id) => navigate(`/inspections/${id}`)}
/>
);
} sessionUrl is the one-shot URL your backend gets back from
sessions.invite() on the Node / Python SDK. The component
mounts the iframe on first render and destroys it cleanly on unmount.
Imperative access via ref
Use a ref when the field-side controls live outside the
iframe (toolbar above the component, modal trigger, etc.):
import { useRef } from "react";
import { NexBasiraSession, type NexBasiraSessionHandle } from "@nexbasira/react";
function Toolbar({ joinUrl }: { joinUrl: string }) {
const session = useRef<NexBasiraSessionHandle>(null);
return (
<>
<button onClick={() => session.current?.requestSnapshot()}>Capture</button>
<button onClick={() => session.current?.openWhiteboard()}>Whiteboard</button>
<button onClick={() => session.current?.endSession()}>End</button>
<NexBasiraSession ref={session} sessionUrl={joinUrl} />
</>
);
} Hook variant
When the wrapper <div> doesn't fit your layout — say
you want the iframe in a flex container with a sidebar — use the hook
+ attach the returned ref to your own host element:
import { useNexBasiraSession } from "@nexbasira/react";
function CustomLayout({ joinUrl }: { joinUrl: string }) {
const [containerRef, widget] = useNexBasiraSession({
sessionUrl: joinUrl,
onSessionComplete: (id) => navigate(`/inspections/${id}`),
});
return (
<div className="grid grid-cols-[1fr_240px] gap-4">
<div ref={containerRef} style={{ minHeight: 600 }} />
<aside>
<button onClick={() => widget.current?.requestSnapshot()}>Snap</button>
</aside>
</div>
);
} Lifecycle callbacks stay fresh across renders
The embed widget is created once per sessionUrl. To make
sure your callbacks always see the latest closure (closing over the
latest state), the wrapper internally stores them in a
useRef and re-reads on every event. You can pass
inline arrow functions safely:
function Page() {
const [count, setCount] = useState(0);
return (
<NexBasiraSession
sessionUrl={joinUrl}
onEvidenceAdded={() => {
// 'count' is always the latest value, not the value at mount
setCount(count + 1);
}}
/>
);
} When does the iframe rebuild?
Only when sessionUrl changes. Every other prop
(callbacks, width, height, expectedOrigin) applies
in-place without remounting. This means changing a callback or
resizing the host doesn't restart the live session — useful when
your app re-renders frequently.
Props
| Prop | Type | Notes |
|---|---|---|
sessionUrl | string | Required. Minted by your backend. |
width / height | string | CSS sizes. Default 100% / 600px. |
expectedOrigin | string | Override iframe origin if hosting on a custom domain. |
className | string | Applied to the wrapper div. |
style | CSSProperties | Applied to the wrapper div. |
on* callbacks | function | Same surface as @nexbasira/embed — see that page. |
Typed handle
import type { NexBasiraSessionHandle } from "@nexbasira/react";
const ref = useRef<NexBasiraSessionHandle>(null);
// ref.current has typed methods: requestSnapshot(), openWhiteboard(), etc. What's next
- @nexbasira/embed — underlying widget
- @nexbasira/node — mint the
sessionUrlin your backend - Source on GitHub