Chat Widget
A controlled React chat widget with streaming Markdown, tool activity, approval cards, localized labels, and responsive dialog layouts.
bunx --bun shadcn@latest add https://krakstack.net/r/chat-widget.jsonOverview
chat-widget installs a controlled assistant dialog and a transport-neutral event reducer. It renders streaming Markdown, tool progress, tool descriptions, approval cards, errors, and responsive minimized or maximized layouts. English and French labels are built in and every label can be overridden.
Install
bunx shadcn@latest add @krak-stack/chat-widgetThe widget installs chat-service automatically and creates components/ui/chat-widget.tsx plus services/chat/state.ts. Add TooltipProvider near your application root if it is not already present.
<TooltipProvider>
<RouterProvider router={router} />
</TooltipProvider>Controlled Setup
The widget does not own a network client. Keep ChatState in Effect Atom, React state, TanStack Query, or another store and implement the four callbacks.
const [state, setState] = useState(initialChatState);
const controller = new AbortController();
const submit = async (action: ChatSubmitAction) => {
setState((current) => ({ ...current, pending: true, error: undefined }));
for await (const event of streamChat({
action,
history: state.history,
signal: controller.signal,
})) {
setState((current) => reduceChatEvent(current, event));
}
setState((current) => ({ ...current, pending: false }));
};
<ChatWidget
state={state}
onSubmit={submit}
onInterrupt={() => controller.abort()}
onReset={() => setState(initialChatState)}
/>;Use a fresh abort controller for each request in production. Add the user message optimistically before starting the stream; the reducer owns assistant messages and tool state emitted by the server.
Event Reduction
Call reduceChatEvent(current, event) for every decoded ChatEvent. The reducer appends text deltas, stores serialized history, updates tool completion, exposes approval requests, and removes empty assistant placeholders after errors.
Approval Actions
When a tool asks for approval, the widget calls onSubmit with approvalId, toolCallId, and the user's decision. Forward approvalId and approved to the server together with the latest history. toolCallId is client-side correlation data.
Messages
The widget selects English or French through Paraglide's current locale and accepts partial overrides.
<ChatWidget
{...props}
messages={{
title: "Support Assistant",
description: "Ask about billing, projects, or account access.",
placeholder: "How can we help?",
}}
/>Exported chatWidgetMessages(locale, overrides) can also resolve labels for tests or surrounding UI.