Skip to content
GitHub

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.

ModeWhere the credential livesWho relays the chatUse it when
sessionA short-lived, thread-bound session pass your server mintsNobody. The browser talks to api.runbear.io directlyProduction: public and signed-in visitors
proxyYour server. The browser sends no Authorization headerYour server relays every requestYou must inspect, log or redact every turn, or you need file attachments or suggested follow-ups
directThe organization API key, in the browserNobodyLocal 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 browser mount() does nothing, so a server-side render shows no widget.
  • Mount into an empty element. From 0.5.0, mounting into an element that already has children removes the existing content, renders the widget, and logs one console warning. On 0.4.0 and earlier it clears the element and renders nothing, and a second mount() throws "Chat widget is already mounted".
  • The headless client is not a public API. The client's api property (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 block to its reply: a confirm question, a select list, a card of details, a list of results, a sortable table or a chart. Response components are in limited availability and are enabled per organization. The widget renders confirm and select from 0.3.0, card from 0.4.0, and list, table, chart and a card's image and links from 0.6.0; see which SDK release renders what. 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 config option
  • Chat API: methods, events and the widget lifecycle
  • Theming: CSS variables, dark mode and class hooks
  • Response components: confirm, select, card, list, table, chart and mcp-app
  • MCP Apps: interactive views from the agent's own MCP servers
  • Errors: session failures, rate limits and RunbearApiError
  • Types: every exported type
  • Changelog: what changed in each release