SDK
Components.
@onpilot/react ships four embed surfaces. They all render the same chat; they differ in the chrome around it.
| Component | Chrome | Use when |
|---|---|---|
CopilotBubble | Floating button + popup | Public website, anonymous or logged-in users |
CopilotSidebar | SDK toggle + slide-out panel | Quick admin embed, you don't want to design the chrome |
CopilotInline | None — fills its container | Embedded in a page region, dashboard tab, modal |
CopilotPanel | None — headless | Full layout control (custom toggle, top-bar pattern) |
All four accept the same base props: identityToken (or identityTokenProvider), theme, primaryColor, context, and onOpen / onClose / onReady / onMessage / onError callbacks. Most styling — agent name, instructions, suggestions — is set in the dashboard, not via props.
Bubble
<CopilotBubble />
Floating button in the corner of the page. Opens a popup on click.
import { CopilotBubble } from "@onpilot/react";
<CopilotBubble
identityToken={token}
position="bottom-right"
width={400}
height={600}
/>;Extra props: position (bottom-right | bottom-left), width, height, defaultOpen.
Sidebar
<CopilotSidebar />
Fixed side panel with the SDK's own toggle button. Pushes page content over when opened (optional).
import { CopilotSidebar } from "@onpilot/react";
<CopilotSidebar
identityToken={token}
position="right"
width={420}
pushContent
/>;Extra props: position (left | right), width, pushContent, enabled, defaultOpen, toggleButton.
Inline
<CopilotInline />
No chrome. Fills its parent container. The chat is just a block in your page.
import { CopilotInline } from "@onpilot/react";
<div className="h-[600px]">
<CopilotInline identityToken={token} />
</div>;Panel
<CopilotPanel />
Headless. No toggle, no built-in close behavior. You control when it mounts and unmounts.
import { CopilotPanel } from "@onpilot/react";
const [open, setOpen] = useState(false);
<button onClick={() => setOpen((o) => !o)}>Assistant</button>;
{open && <CopilotPanel identityToken={token} width={420} height="100vh" />}CopilotInline or CopilotSidebar if you need the hook.Custom layouts
Top-bar assistant pattern
A button in your top bar opens a chat panel anchored to the right edge of the viewport. The iframe's own X button emits a postMessage — you listen for it and update your state.
"use client";
import { useEffect, useState } from "react";
import { CopilotPanel } from "@onpilot/react";
const DASHBOARD_URL = "https://chat.onpilot.ai";
export function AssistantButton({ identityToken }: { identityToken: string }) {
const [open, setOpen] = useState(false);
useEffect(() => {
const dashboardOrigin = new URL(DASHBOARD_URL).origin;
function handler(event: MessageEvent) {
if (event.origin !== dashboardOrigin) return;
if (event.data?.type === "onpilot:close") setOpen(false);
}
window.addEventListener("message", handler);
return () => window.removeEventListener("message", handler);
}, []);
return (
<>
<button onClick={() => setOpen((o) => !o)}>Assistant</button>
{open && (
<div className="fixed right-0 top-0 h-screen w-[420px] shadow-xl">
<CopilotPanel identityToken={identityToken} width="100%" height="100%" />
</div>
)}
</>
);
}message event handler that doesn't check origin is a security trap — any page on the web can postMessage to your window.