Screens
Support Widget
A support bot embedded on a website — launcher bubble, quick replies, order lookup, handoff to a human and a rating at the end.
2 variantsMIT
Preview
New season
Gear for the long way round.
Free returns for 60 days, repairs for life.
- Trail Runner 2$139
- Summit Shell Jacket$249
- Ridge 28L Pack$119
Installation
Copies the screen into components/ and installs every component it uses, with their dependencies and theme tokens.
pnpm dlx shadcn@latest add https://tuffui.com/r/support-widget.jsonUsage
Pass your own data and wire the callbacks to your agent runtime.
import { SupportWidget } from "@/components/support-widget";
export function SiteWithSupport({ chat, children }: { chat: SupportChat; children: React.ReactNode }) {
return (
<SupportWidget
data={{
bot: { name: "Ridge" },
human: chat.assignee ? { name: chat.assignee.name, role: "Support team" } : undefined,
visitor: { name: chat.visitor.name },
// content is any React node — order cards, questions, forms.
messages: chat.messages.map((m) => ({
id: m.id,
role: m.from === "visitor" ? "user" : "assistant",
author: m.from === "agent" ? chat.assignee?.name : undefined,
at: m.createdAt,
content: m.text,
})),
quickReplies: chat.messages.length < 2 ? chat.quickReplies : undefined,
mode: chat.assignee ? "human" : chat.queued ? "waiting" : chat.botTyping ? "typing" : "bot",
wait: chat.queueEstimate,
askRating: chat.closed,
unread: chat.unread,
}}
onSend={(text) => chat.send(text)}
onRate={(rating) => chat.rate(rating)}
onClose={() => chat.end()}
>
{children}
</SupportWidget>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| data* | SupportWidgetData | — | Bot, the person who takes over, the visitor, the turns, quick replies and the current mode. |
| children | ReactNode | — | The host page the widget floats over. It stays usable while the panel is open. |
| open / defaultOpen / onOpenChange | boolean / boolean / (open: boolean) => void | — / false / — | Whether the panel is open. The launcher toggles it, Esc and the minimise button close it. |
| onSend | (text: string) => void | — | A message from the composer or a quick reply. |
| onRate | (feedback) => void | — | The 1–5 rating, reasons and note from the end-of-chat rating. |
| onClose | () => void | — | The end-chat button. Minimising only hides the panel. |
| className | string | — | Extra classes for the root. |
type SupportPerson = { name: string; avatar?: string; role?: string };
type SupportWidgetData = {
bot: SupportPerson;
human?: SupportPerson; // set message.author to human.name for their turns
visitor: { name: string };
messages: ThreadMessage[]; // from chat-thread; content is a ReactNode
quickReplies?: PromptSuggestion[]; // chips under the last bot message
mode?: "bot" | "typing" | "waiting" | "human";
wait?: string; // queue estimate, e.g. "~2 min"
askRating?: boolean;
unread?: number; // badge on the launcher while closed
};
// The widget fills its parent and lays out by its own width: from 28rem the panel
// floats bottom-right over the page, below that it fills the screen.