Open source · React · MIT
Embeddable React AI chat widget
@anter/ai-chat-sdk
A drop-in React chat widget for any website. Stream conversations from any Anter agent — reasoning steps, tool calls, and rich artifacts included — and theme it to your brand with CSS variables. You wire your backend through one typed adapter; nothing else is hardcoded.
Key Features
One small dependency
A headless hook plus prebuilt widget and full-page chat UI. Streams Markdown, reasoning steps, tool calls, and artifacts out of the box.
Adapter, not lock-in
You implement one `ChatAdapter.sendMessage` that returns a stream. Auth, headers, and proxying stay in your control — secrets never ship to the browser.
Themeable by CSS variables
Drives off `--chat-*` custom properties and a no-reset stylesheet, so it inherits your brand without fighting your global CSS.
Install
Not on npm yet — install straight from the public repo. It builds itself on install, so no extra step is needed. Import the stylesheet once at your app entry; use styles-no-base.css to avoid global CSS resets and keep full control of your own cascade.
# Not published to npm yet — install from the public repo.
# The package builds itself on install.
# npm
npm install github:anter-ai/ai-chat-sdk
# pnpm
pnpm add github:anter-ai/ai-chat-sdk
# Pin to a commit for a reproducible install:
npm install github:anter-ai/ai-chat-sdk#<commit-sha>
# Load the widget styles once, at your app entry:
import "@anter/ai-chat-sdk/styles-no-base.css";React AI Chat SDK Tutorial: How to Embed a Streaming Chat Widget
To embed a streaming AI chat widget into any React application, install @anter/ai-chat-sdk, import its CSS variable stylesheet once, and connect your backend using a single typed ChatAdapter implementation.
- •Backend-Agnostic Adapter: Your server manages authentication and API keys while streaming SSE chunks to the UI via
ChatAdapter.sendMessage. - •Theme Custom Properties: Customize colors and typography using
--chat-*CSS variables without breaking your existing app styles. - •Built-In Artifact & Reasoning Tracing: Automatically renders Markdown streams, tool call execution accordions, and sub-agent handoffs.
Quickstart Code
Implement a ChatAdapter that streams from your agent endpoint, then wrap your app in ChatProvider and drop in a ChatWidget.
1 · Adapter
import type { ChatAdapter, MessagePayload } from "@anter/ai-chat-sdk/types";
export class LandingChatAdapter implements ChatAdapter {
constructor(private readonly agentId: string) {}
private get streamPath(): string {
return `/api/v1/external/agent-runner/agents/${this.agentId}/run-stream`;
}
async sendMessage(payload: MessagePayload): Promise<ReadableStream<Uint8Array>> {
const res = await fetch(this.streamPath, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: payload.message,
organizationId: payload.organizationId,
// contextVariables flow through to the agent. Pass per-session identity
// so the runner can scope memory/history to this conversation.
contextVariables: {
...(payload.contextVariables ?? {}),
userId: `chat-widget-${payload.sessionId}`,
sessionId: payload.sessionId,
},
}),
});
if (!res.ok) throw new Error(`sendMessage failed: ${res.status}`);
if (!res.body) throw new Error("sendMessage: missing response body");
return res.body;
}
}2 · Provider + widget
import { ChatProvider, ChatWidget } from "@anter/ai-chat-sdk";
import { LandingChatAdapter } from "./lib/landing-adapter";
const organizationId = (import.meta.env.VITE_ORG_ID as string | undefined) ?? "anter";
const chatAgentId = import.meta.env.VITE_CHAT_AGENT_ID as string;
const adapter = new LandingChatAdapter(chatAgentId);
<ChatProvider
organizationId={organizationId}
adapter={adapter}
config={{
theme: "light",
enableModelSelector: false,
enableSlashCommands: false,
enableCommandPalette: false,
enableFileUpload: false,
enableSlashFocusShortcut: false,
enableArtifacts: true,
}}
strings={{
composerPlaceholder: "Ask a question...",
footerDisclaimer: "AI responses can contain mistakes.",
}}
>
{page}
<ChatWidget position="bottom-right" title="Anter" />
</ChatProvider>/api/...) and inject x-project-id / x-api-key there — never in browser code.Configuration
Pass a config object to ChatProvider to tune the experience. The "Landing value" column shows the choices used on this very site.
| Option | Landing value | Why used | Other options |
|---|---|---|---|
enableModelSelector | false | Keep model choice controlled by backend/flow configuration. | Set `true` to expose model switching to end users. |
enableSlashCommands | false | Simplify consumer-facing UX. | Set `true` for power-user workflows and command shortcuts. |
enableCommandPalette | false | Avoid advanced command UI in public marketing chat. | Enable for internal apps or operator consoles. |
enableFileUpload | false | Limit public-surface risk and keep scope to text chat. | Enable for document workflows once validation is in place. |
enableSlashFocusShortcut | false | Prevent accidental shortcut conflicts on landing pages. | Enable for app-like experiences with keyboard-heavy users. |
enableArtifacts | true | Allow rich response rendering with artifacts. | Disable if your use case requires strict plain-text output only. |
Theming
The widget renders against --chat-* CSS custom properties (accent, surfaces, borders, message text). Override them in your own stylesheet to match your design system — light and dark adapt automatically. Because the SDK ships its styling as raw, prefixed ais-* classes rather than relying on a Tailwind runtime, it drops cleanly into any host without a build-time theme step.
Guides & Where it's used
Use case
Build Ask Anter
The exact end-to-end flow we use for the Ask Anter widget on this site: set up the agent stack, then wire it into a production chat widget with @anter/ai-chat-sdk.
Developer Guide
React AI Chatbot Widget Guide
Step-by-step external guide covering `@anter/ai-chat-sdk` installation, custom `ChatAdapter` implementation, and streaming widget setup.
Read Google Doc Guide