@flutchai/chat-react
React chat UI for a Flutch agent. Themeable with CSS variables; the markdown renderer, buttons, message rows and attachment renderers can all be replaced.
The UI talks to your backend, never to Flutch directly. Your backend holds the Flutch API key and forwards requests (see @flutchai/chat-core).
Browser (this package) ──► your backend (adds X-API-Key + userId) ──► api.flutch.ai
Quick start
import { ChatProvider, Chat } from "@flutchai/chat-react";
import { createFlutchClient } from "@flutchai/chat-core";
import "@flutchai/chat-react/styles.css";
const client = createFlutchClient({
baseUrl: "/api/chat", // your backend's proxy
agentId: "67b88f7ec6092d7bc711a934",
credentials: "include", // if your proxy authenticates with a session cookie
});
export function Support() {
return (
<div style={{ height: 600 }}>
<ChatProvider client={client}>
<Chat showThreads />
</ChatProvider>
</div>
);
}
The chat fills its parent, so give the parent a height. It restores the previous conversation on mount (the current thread id is kept in localStorage; pass storage to ChatProvider to use something else).
Theming
Override the variables on the chat element or any ancestor:
.my-chat {
--flutch-primary: #0a7a5a;
--flutch-on-primary: #fff;
--flutch-radius: 8px;
--flutch-font: "Inter", sans-serif;
}
| Variable | Default | |
|---|---|---|
--flutch-primary / --flutch-on-primary | #4f46e5 / #fff | Send button, accents |
--flutch-user-bg / --flutch-user-text | primary / on-primary | Your user's bubbles |
--flutch-assistant-bg | --flutch-surface | Agent bubbles |
--flutch-bg, --flutch-surface, --flutch-text, --flutch-muted, --flutch-border | light | Surfaces and text |
--flutch-danger, --flutch-danger-bg | red | Error banner |
--flutch-radius, --flutch-gap, --flutch-font, --flutch-font-size | 14px, 12px, system, 15px | Shape and type |
--flutch-chart-1 … --flutch-chart-8 | primary, then a fixed palette | Chart series |
Dark mode follows the OS. Force one with <Chat theme="dark" />.
Replacing parts
import ReactMarkdown from "react-markdown";
<ChatProvider
client={client}
labels={ruLabels} // or your own strings
components={{
Markdown: ({ text }) => <ReactMarkdown>{text}</ReactMarkdown>,
Button: MyDesignSystemButton,
Message: ({ message }) => ( // wrap the default row
<div className="row"><MessageItem message={message} /></div>
),
}}
attachmentRenderers={{
product_link: ProductCard, // add or override by attachment type
}}
>
- Markdown. The default shows plain text, so there is no markdown dependency and nothing to sanitize. Plug in
react-markdown(it escapes HTML by default) if your agent answers with markdown. Do not render agent text withdangerouslySetInnerHTML. - Attachments. Built in:
image,video,audio,voice,file,button(one button or an array of them),suggestion,citation,card,gallery,article_link(a report card: a link when the value has aurl, otherwise just the title),questionandchart(see below). Other types (product_link,checkout_link,payment_link,order_link,recipe_card,lead_form, deeplinks) have no default because they depend on your storefront: register a renderer. Unknown types are skipped, and one malformed attachment never breaks the conversation. All URLs coming from the agent go throughsafeUrl(onlyhttp,https,mailto,teland relative links). - Questions. A
questionattachment renders as a card with one to four questions: single or multiple choice, plus a free-text "Other" unlessallowOther: false. The answer goes out as the user's next message, in the format the agent expects (picked labels joined with,; oneheader: answerline per question when there are several). The reply buttons that come with it for simpler clients (metadata.fallbackFor: "question") are hidden. Once the user has written anything after the question, the card is read-only, also after a reload.QuestionCardis exported if you want it outside the message list. - Charts. A
chartattachment (line,area,bar,pie) is drawn as SVG, with no chart library. Bar charts takeoptions.stackedandoptions.horizontal(withoptions.labelWidth); values are formatted withoptions.currency,options.percentageoroptions.unit; the grid hides withshowGrid: false, and the legend shows for several named datasets or a pie (showLegendforces it either way). Hovering a bar, point or slice shows its value, and the values are also listed in a visually hidden table for screen readers. Colors come from the dataset (plain color values only) or from the--flutch-chart-*palette. The chart follows the width of the message bubble. - Thread list.
ThreadListgroups conversations by the day of their last activity (Today, Yesterday, Previous 7 days, Older), loads further pages with "Show more", shows the loading and empty states, and marks the conversation whose reply is being generated. A conversation gets its title when the server names it (thread_renamed) and moves to the top when a reply lands. PassonSelectto react to a pick, e.g. to close a drawer on mobile. Renaming and deleting conversations are not in the API yet. - Layout.
<Chat>is a convenience. ComposeMessageList,Composer,ThreadList,ErrorBannerandEscalationNoticeyourself for another layout, or read state withuseChat(selector).
Hooks
const messages = useChat(s => s.messages);
const store = useChatStoreApi();
store.getState().send({ text: "Hello" }); // programmatic send
Full state and actions: see ChatStore in @flutchai/chat-core.