Web SDK overview
Embed a Runbear agent as a chat widget in your own web app with the Runbear React SDK, and pick how it authenticates.
On this page
The Runbear React SDK (@runbear-io/react) embeds a Runbear agent as a chat widget inside your
own web app: a complete chat UI with streaming replies, conversation history, the agent's thinking
and tool progress, and clickable response components. The widget
talks to the same /v1 API described in the REST API overview.
The package bundles its own copy of React and has no peer dependencies, so it works in any web app, React or not. You create a client, create a chat, and mount it into an element on your page.
How the widget authenticates#
A Runbear API key is an organization-level credential, so it must never be shipped in public
browser JavaScript. The SDK offers three modes; pick one with the auth option.
| Mode | Where the credential lives | Who relays the chat | Use it when |
|---|---|---|---|
session | A short-lived, thread-bound session pass your server mints | Nobody. The browser talks to api.runbear.io directly | Production: public and signed-in visitors |
proxy | Your server. The browser sends no Authorization header | Your server relays every request | You must inspect, log or redact every turn, or you need file attachments or suggested follow-ups |
direct | The organization API key, in the browser | Nobody | Local prototyping and fully internal tools only |
Session mode needs @runbear-io/react 0.2.0 or newer. Authentication
covers all three modes and the session endpoint you write for session mode.
Quick start#
This React component mounts the widget in session mode. /api/runbear/session is your
endpoint: it authenticates the visitor and mints the session pass with your API key.
Your session endpoint has its full
implementation.
import Runbear, { SessionDeclinedError } from "@runbear-io/react"
import { useEffect, useRef } from "react"
export function AgentWidget() {
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!containerRef.current) return
const client = new Runbear({
assistantId: "0fade940-133f-49e6-bf4b-8f662186479b",
auth: {
mode: "session",
fetchSessionToken: async ({ resumeToken, threadId, signal }) => {
const res = await fetch("/api/runbear/session", {
method: "POST",
headers: { "content-type": "application/json" },
credentials: "include", // your own session cookie
body: JSON.stringify({ resumeToken, threadId }),
signal,
})
if (res.status === 401 || res.status === 403) {
// Your endpoint refused this visitor: end the session now.
throw new SessionDeclinedError(`session refused: ${res.status}`)
}
// Anything else is treated as transient and retried with backoff.
if (!res.ok) throw new Error(`session endpoint failed: ${res.status}`)
return await res.json()
},
},
})
const chat = client.createChat()
chat.mount(containerRef.current)
return () => chat.destroy()
}, [])
return <div ref={containerRef} style={{ width: "100%", height: "100vh" }} />
}Throw SessionDeclinedError when your endpoint refuses the visitor. It ends the session at once;
any other error is retried. See Declining a session.
Without React#
The widget mounts imperatively, so a page without React uses the same calls. Run this in the browser once the element exists:
import { Runbear, SessionDeclinedError } from "@runbear-io/react"
const client = new Runbear({
assistantId: "0fade940-133f-49e6-bf4b-8f662186479b",
auth: {
mode: "session",
fetchSessionToken: async ({ resumeToken, threadId, signal }) => {
const res = await fetch("/api/runbear/session", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ resumeToken, threadId }),
signal,
})
if (res.status === 401 || res.status === 403) {
throw new SessionDeclinedError(`session refused: ${res.status}`)
}
if (!res.ok) throw new Error(`session endpoint failed: ${res.status}`)
return await res.json()
},
},
})
const chat = client.createChat()
chat.on("containerReady", ({ threadId }) => console.log("chatting on", threadId))
// An empty element with a height, such as <div id="runbear-chat" style="height: 600px">
chat.mount("#runbear-chat")The package is ESM-only, so load it through your bundler or another tool that resolves npm packages.
Limitations#
- One widget per page. The widget renders into a fixed container id and keeps a module-level reference to its UI, so a second widget on the same page removes the first one's container.
- Browser only. Construct the client and call
mount()in the browser: in a React effect, or after the page has loaded. Outside a browsermount()does nothing, so a server-side render shows no widget. - Mount into an empty element. On 0.4.0 and earlier, the current release, mounting into an
element that already has children clears it and renders nothing, and a second
mount()throws "Chat widget is already mounted". Coming in 0.5.0 (not yet released), the widget removes the existing content, renders, and logs one console warning. - The headless client is not a public API. The client's
apiproperty (runbear.api.v1.*) is what the widget uses internally. It is not a supported public surface and can change in any release; call the REST API from your server instead.
Response components#
An agent can attach a small interactive block to its reply: a confirm question, a select list or
a card of details. Response components are in limited availability and are enabled per
organization. The widget renders confirm and select from 0.3.0 and card from 0.4.0. See
Response components for how the widget renders them, and the
Response Components API for the per-agent setting.
Reference#
- Install: the registry, CI, CSP and version support
- Authentication: session, proxy and direct modes, and your session endpoint
- Configuration: every
configoption - Chat API: methods, events and the widget lifecycle
- Theming: CSS variables, dark mode and class hooks
- Response components:
confirm,selectandcard - Errors: session failures, rate limits and
RunbearApiError - Types: every exported type
- Changelog: what changed in each release
Related#
- REST API overview
- Authentication and API keys: the key your session endpoint uses
- Errors and limits: rate limits shared by every visitor of a widget
- OpenAPI reference: exact schemas for
POST /v1/sessionsandPOST /v1/sessions/refresh