@onpilot/react
v0.3.0
Published
React SDK for OnPilot — tenant-key copilot widgets
Maintainers
Readme
@onpilot/react
React SDK for embedding OnPilot copilots into your app.
Four drop-in components (CopilotBubble, CopilotSidebar, CopilotInline,
CopilotPanel) that render the chat iframe. Authentication is handled via
a tenant-signed identity JWT — the SDK exchanges it for a short-lived
session token behind the scenes.
Install
npm install @onpilot/react @onpilot/nodeHow it works
- Your server uses
@onpilot/nodeto sign a tenant identity JWT naming the targetcopilotId. TheembedSecretnever reaches the browser. - Your client passes that JWT to an
@onpilot/reactcomponent asidentityToken(oridentityTokenProviderfor refresh). - The component calls
POST /api/v1/embed/resolveto exchange the JWT for a session token and chat URL, then renders the iframe.
Flat model — you own the mapping
OnPilot has no workspace/org concept at the embed boundary. If different
parts of your app should talk to different copilots, keep a
workspace → copilotId mapping on your side and look it up before signing.
Same contract Botpress uses (their clientId == our copilotId).
Option A — pre-signed token (server-rendered)
Sign once on the server, pass the JWT to the component:
// app/copilot/page.tsx (Next.js RSC)
import { OnPilot } from "@onpilot/node";
import { CopilotBubble } from "@onpilot/react";
export default async function Page() {
const onpilot = new OnPilot({
tenantId: process.env.ONPILOT_TENANT_ID!,
embedSecret: process.env.ONPILOT_EMBED_SECRET!,
});
const identityToken = onpilot.signIdentityToken({
copilotId: process.env.ONPILOT_COPILOT_ID!, // or look up per workspace
user: { id: "user-123", name: "Ada", email: "[email protected]", role: "admin" },
});
return <CopilotBubble identityToken={identityToken} />;
}Option B — async provider (refreshes on expiry)
Return a fresh JWT from your backend each time:
"use client";
import { CopilotSidebar } from "@onpilot/react";
export function ChatSidebar() {
return (
<CopilotSidebar
identityTokenProvider={async () => {
const r = await fetch("/api/onpilot/token");
const { identityToken } = await r.json();
return identityToken;
}}
/>
);
}The SDK re-invokes the provider automatically before the JWT expires.
Components
All four components share the same auth props (identityToken or
identityTokenProvider) plus their own visual props.
<CopilotBubble />
Floating chat button + popover in the corner of the screen.
<CopilotBubble
identityToken={jwt}
position="bottom-right" // or "bottom-left"
width={400}
height={600}
theme="light"
primaryColor="#6366f1"
defaultOpen={false}
/><CopilotSidebar />
Slide-in panel fixed to the side of the viewport.
<CopilotSidebar
identityToken={jwt}
position="right" // or "left"
width={400}
pushContent // push page content when open
defaultOpen={false}
theme="light"
/><CopilotInline />
Chat UI with OnPilot's default chrome, sized to its container.
<CopilotInline identityToken={jwt} width="100%" height={500} theme="light" /><CopilotPanel />
Headless chat UI — no floating button, no toggle, fills the container. Ideal when you already have your own sidebar or panel shell.
<CopilotPanel
identityToken={jwt}
context={{ recordType: "company", recordId: "abc-123", recordData: { name: "Acme" } }}
/>Shared props
| Prop | Type | Description |
| --- | --- | --- |
| identityToken | string | Pre-signed tenant JWT. Use this or identityTokenProvider. |
| identityTokenProvider | () => Promise<string> | Async factory — called on mount and on refresh. |
| dashboardUrl | string | Where the resolve endpoint lives. Defaults to https://chat.onpilot.ai. |
| theme | "light" \| "dark" \| "system" | |
| primaryColor | string | |
| locale | string | |
| context | CopilotContext | CRM/app context forwarded to the copilot via postMessage. |
| onReady, onOpen, onClose, onMessage, onError | callbacks | Lifecycle events. |
| className | string | |
Hooks
useOnPilot()
Imperatively open/close/toggle the copilot from any child component:
import { useOnPilot } from "@onpilot/react";
function OpenButton() {
const { open, isReady, setContext } = useOnPilot();
return <button disabled={!isReady} onClick={open}>Ask OnPilot</button>;
}useResolvedSession({ identityToken, identityTokenProvider, dashboardUrl, onError })
Low-level hook used internally — exposes { session, isLoading, error, refresh }
if you need to drive your own rendering.
License
MIT
