All Taste components
Communication
Chat thread
A conversation with a client: message bubbles, times, an attachment and a reply box.
incoming · outgoing · day divider · attachment · read receipt · composer
PN
Priya Nair
Kaveri Foods · Online
Today
Hi! Could we move the tasting session to Saturday?
10:12Sure, Saturday at 11:00 works for us.
I have attached the revised menu with the new prices.
PDF
10:14 · ReadMenu-v3.pdf
420 KB
Perfect. See you on Saturday, thank you!
10:20"use client";
import { useState } from "react";
import { MoreHorizontal, Paperclip, SendHorizontal } from "lucide-react";
import { cn } from "@/lib/utils";
export interface ChatMessage { id: string; from: "me" | "them"; text: string; time: string; read?: boolean }
const sample: ChatMessage[] = [
{ id: "1", from: "them", text: "Hi! Could we move the tasting session to Saturday?", time: "10:12" },
{ id: "2", from: "me", text: "Sure, Saturday at 11:00 works for us.", time: "10:14" },
{ id: "3", from: "me", text: "I have attached the revised menu with the new prices.", time: "10:14", read: true },
{ id: "4", from: "them", text: "Perfect. See you on Saturday, thank you!", time: "10:20" },
];
export function ChatThread({ name = "Priya Nair", subtitle = "Kaveri Foods · Online", messages = sample, onSend }:
{ name?: string; subtitle?: string; messages?: ChatMessage[]; onSend?: (text: string) => void }) {
const [draft, setDraft] = useState("");
const send = (e: React.FormEvent) => {
e.preventDefault();
if (!draft.trim()) return;
onSend?.(draft.trim());
setDraft("");
};
const initials = name.split(" ").map((w) => w[0]).join("").slice(0, 2);
return (
<section className="flex w-full max-w-[440px] flex-col overflow-hidden rounded-lg border border-border bg-bg">
<header className="flex items-center gap-2.5 border-b border-border px-4 py-3">
<span className="relative grid size-9 place-items-center rounded-full bg-primary-soft text-[13px] font-medium text-primary-soft-fg">
{initials}
<span className="absolute bottom-0 right-0 size-[9px] rounded-full border-2 border-bg bg-success" aria-hidden />
</span>
<div className="min-w-0 flex-1">
<p className="font-medium">{name}</p>
<p className="text-[12.5px] text-fg-subtle">{subtitle}</p>
</div>
<button aria-label="More options" className="grid size-7 place-items-center rounded text-fg-muted hover:bg-surface-2">
<MoreHorizontal className="size-4" />
</button>
</header>
<div className="flex flex-col gap-1 bg-surface p-4" role="log" aria-label={"Conversation with " + name}>
<span className="mb-2 self-center rounded-full border border-border bg-bg px-2.5 py-0.5 text-[11.5px] text-fg-subtle">Today</span>
{messages.map((m, i) => {
const mine = m.from === "me";
const lastOfRun = messages[i + 1]?.from !== m.from;
return (
<div key={m.id} className={cn("flex flex-col", mine ? "items-end" : "items-start")}>
<p className={cn(
"max-w-[80%] rounded-lg px-3 py-2 text-[13.5px] leading-[1.45] [overflow-wrap:anywhere]",
mine ? "rounded-br-sm bg-primary text-primary-fg" : "rounded-bl-sm border border-border bg-bg",
)}>{m.text}</p>
{lastOfRun && (
<span className="mx-1 mb-2 mt-1 text-[11px] tabular-nums text-fg-subtle">{m.time}{mine && m.read ? " · Read" : ""}</span>
)}
</div>
);
})}
</div>
<form onSubmit={send} className="flex items-center gap-2 border-t border-border p-3">
<button type="button" aria-label="Attach a file" className="grid size-9 shrink-0 place-items-center rounded text-fg-muted hover:bg-surface-2">
<Paperclip className="size-4" />
</button>
<input value={draft} onChange={(e) => setDraft(e.target.value)} placeholder="Write a reply…" aria-label="Message"
className="h-9 min-w-0 flex-1 rounded border border-border-strong bg-bg px-3 text-sm outline-none placeholder:text-fg-subtle focus:border-primary focus:ring-[3px] focus:ring-primary/20" />
<button type="submit" aria-label="Send" className="grid size-9 shrink-0 place-items-center rounded bg-primary text-primary-fg hover:bg-primary-hover">
<SendHorizontal className="size-4" />
</button>
</form>
</section>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.