Support Chat
NewA floating support widget. The launcher morphs into a chat panel with an agent header, bubbles that spring in, a typing indicator, quick reply chips and a send button that flies its plane.
motionlucide-reactutilsavatarbadgebuttoncardinput-groupitemseparator@soultel/icon-send@soultel/use-reduced-motion-safe
Installation
$ pnpm dlx shadcn@latest add @soultel/support-chatNo namespace set up? npx shadcn@latest add https://asafefe.dev/r/support-chat.json works too.
Manual installation
- 1. Install the dependencies.
terminal1 lines npm install motion lucide-react - 2. Copy the source from the Code tab into components/support-chat.tsx.
Usage
import { SupportChat, type ChatMessage } from "@/components/support-chat"
export function Support() {
const [messages, setMessages] = useState<ChatMessage[]>([
{ id: "hi", from: "agent", text: "Hi! How can we help today?", replies: ["Track my order", "Return an item"] },
])
const [typing, setTyping] = useState(false)
return (
<SupportChat
team="Northside support"
agent={{ name: "Maya", role: "Typically replies in 2 min", avatar: "/team/maya.jpg" }}
messages={messages}
onMessagesChange={setMessages}
typing={typing}
onSend={async (text) => {
setTyping(true)
const reply = await fetch("/api/support", { method: "POST", body: JSON.stringify({ text }) }).then((r) => r.json())
setTyping(false)
setMessages((m) => [...m, { id: reply.id, from: "agent", text: reply.text }])
}}
/>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| agent | ChatAgent | - | name, and optional role, avatar (photo URL, initials as fallback) and status ("online" | "away" | "offline"). |
| team | string | - | Header title, e.g. "Northside support". Defaults to the agent's name. |
| messages / defaultMessages | ChatMessage[] | - | Controlled or initial messages: { id, from: "agent" | "user", text, time, replies }. replies shows chips under the latest agent message. |
| onMessagesChange | (messages: ChatMessage[]) => void | - | Called when the visitor sends a message. |
| onSend | (text: string) => void | Promise<void> | - | Called with each sent message. A promise dims the bubble until it settles; a rejection offers a retry. |
| typing | boolean | false | Shows the typing indicator. |
| quickReplies | string[] | - | Chips shown before the visitor has written anything. |
| open / defaultOpen | boolean | false | Controlled or initial open state. |
| onOpenChange | (open: boolean) => void | - | Called by the launcher, the minimize button and Escape. |
| unread | number | 0 | Count on the launcher while closed. |
| position | "fixed" | "absolute" | "fixed" | Pin to the viewport corner, or to the nearest positioned parent. |
| placeholder | string | "Write a message" | Input placeholder. |
