Skip to main content
Two React components ship from @waniwani/sdk/chat. Pick based on who runs the chat backend: The React components mount inline: they render wherever you place them in your layout, and you control the container. There is no launcher or floating bubble in the React path — for that, use the embed script in floating mode.
Platform feature. Requires a Waniwani project token (wwp_...). Get one at app.waniwani.ai or read more about the Platform.

Install

Add the stylesheet to your app once (e.g. in _app.tsx or your root layout):

WaniwaniChat

The hosted chat. Pass your project token and channel ID and it talks to app.waniwani.ai directly — no API route, no proxy, no backend work on your side.
The system prompt, welcome message, placeholder, suggestions, thread history and tool-call display are configured per channel in the dashboard, so business users can change them without a deploy. Theming has no dashboard equivalent — set it in code via overrides.appearance.

Sizing and placement

WaniwaniChat fills its parent. Put it wherever you want it and size the container:
To use it as a full-width page hero, give the wrapper the height you want and let the chat fill it. className is applied to the root element if you’d rather style it directly.

Per-page overrides

The dashboard is the source of truth. Use overrides only for a local tweak that doesn’t justify a separate channel:

Tracking from the host page

The ref exposes a ChatHandle with track and identify alongside the chat controls. identify takes the user id as its first argument, with optional traits second — it is not an object:
Both are present on WaniwaniChat only — the bare ChatEmbed primitive has no Waniwani credential to send events with. See Identify.

ChatEmbed

A bare-bones primitive for when you run the chat backend yourself. api is required and there is no default; pass any endpoint implementing the Vercel AI SDK chat protocol and the component handles the rest.
Reach for ChatEmbed only when self-hosting. If you’re using the Waniwani Platform, WaniwaniChat gives you the same UI plus dashboard configuration, visitor correlation, and tracking.
Both components accept a visitorId prop to override the anonymous id the widget generates with one you already track (a PostHog / Amplitude / Segment distinct id, your own cookie). Waniwani correlates its sessions and events to the same visitor your analytics sees, and your server-side MCP tools and flows read it back as context.waniwani.visitorId. Leave it unset to keep the auto-generated, localStorage-persisted id. visitorId accepts a string, or a resolver that returns one. The resolver may be sync or async, which is handy when your analytics SDK only exposes its id after it bootstraps:
A blank or failed result is ignored, so a not-ready id never wipes a good one. The value is read live on every request, so updating it applies to the next message. See Sessions for how the three correlation ids fit together.
For an expensive async resolver, pass a stable reference (useCallback) so it isn’t re-invoked on every render.

Attachments

ChatEmbed takes the documents module’s state as a prop, since it fetches no remote config of its own:
The composer offers a paperclip, a drop target and paste only while enabled is true, and the upload endpoint enforces the same flag. WaniwaniChat and the script embed read all of this from the dashboard, so they need none of it. See Documents for what an agent does with the files.

Authentication

headers goes out with every chat request, and document uploads reuse it, so one bearer usually covers both.
Set uploadHeaders when your upload endpoint takes a different credential than your chat endpoint. It is merged over headers on the document upload and discard calls, and leaves the chat request alone.
Uploads there go out with the upload token and still carry X-Tenant-Id. Only the names you repeat are replaced, and the match ignores case, so authorization overrides Authorization. Both land in the browser, so put only public credentials in them.

Theming

Both components take an appearance preset (light, dark, or auto) with optional per-property overrides layered on top:
auto follows the host’s prefers-color-scheme. variables accepts every ChatTheme token — colors, message-bubble radius/padding/max-width, base font size and line height. The full token and CSS-variable list lives on Theming & customization.

Deep customization

Two escape hatches when tokens aren’t enough:
  • appearance.assistantBubble — opt-in filled bubble for assistant replies (plain text by default), styled by assistantBubbleColor / assistantBubbleTextColor.
  • classNames — per-slot class overrides merged onto the widget’s own classes: root, header, message, userBubble, assistantBubble, input. Pass it directly on ChatEmbed, or as overrides.classNames on WaniwaniChat.
Recipes and the list of what remains non-themeable: Theming & customization.

Mirroring events into your analytics

The hosted WaniwaniChat component accepts an onEvent prop that fires on chat lifecycle events (messages, session start, errors, link clicks) so you can mirror widget activity into your own analytics — Segment, Amplitude, gtag — with the page’s identity attached automatically. ChatEmbed, the bring-your-own-backend primitive, does not expose it. See Widget events (onEvent).