A messaging section where a hairline list of saved replies, each with what it is for and how often it is used, swaps a bordered preview card showing the message and the fields it fills in from the record.
A messaging section where a hairline list of saved replies, each with what it is for and how often it is used, swaps a bordered preview card showing the message and the fields it fills in from the record.
Upgrade to Pro
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://ui.beste.co/r/chat29?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/chat29?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/chat29.tsx plus the badge23 and button21 components it uses for the eyebrow and the action.
The installed file exports chat29Demo alongside the block: the exact props behind the preview above. Spread it to get a working library in one line.
import { Chat29, chat29Demo } from "@/components/beste/block/chat29";
export default function RepliesPage() {
return <Chat29 {...chat29Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Chat29 } from "@/components/beste/block/chat29";
export default function RepliesPage() {
return (
<Chat29
badge={{ label: "Saved replies" }}
heading="The four things reception types forty times a week"
description="Written once, kept where the conversation is, and filled in from the record."
templates={[
{
name: "Confirming a move",
purpose: "When a member asks to change an appointment and a slot is free",
uses: "412 times this month",
body: "Hello {first_name}, that is moved. You are now booked for {appointment_time}.",
variables: ["first_name", "appointment_time"],
},
{
name: "Nothing free this week",
purpose: "When the member wants a slot the rota cannot offer",
uses: "188 times this month",
body: "Hello {first_name}, I have put you on the waiting list.",
variables: ["first_name"],
},
]}
previewLabel="How it goes out"
variablesLabel="Filled in from the record"
button={{ label: "See the reply library", href: "/product/replies" }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string } | – | Eyebrow above the hairline rule, rendered through Badge23 |
heading | string | – | Section heading in the left column of the header |
description | string | – | Supporting paragraph, right-aligned from md up |
templates | Template[] | [] | Saved replies, the first one starts selected |
previewLabel | string | – | Small label above the message preview |
variablesLabel | string | – | Small label above the variable chips |
button | { label: string; href: string } | – | Outline action under the list |
className | string | – | Extra classes for the outer section |
type ActionLink = {
label: string;
href: string;
};
type Template = {
name: string;
purpose: string;
uses: string;
body: string;
variables: string[];
};body is rendered as plain text, braces and all. The block does not parse or highlight the placeholders, so what you see in the preview is exactly the stored template.variables is a separate list rather than being extracted from body. That means the two can drift, so keep them in step, and it also lets you list a variable that appears more than once only once.useState holding an index, starting at 0. There is no onChange prop and no controlled mode.bg-border to bg-primary, alongside a bg-muted fill on the row. State never depends on the accent alone.px-4, so the selected fill extends past the text rather than cutting tight against it.flex flex-col with the body marked flex-1, so the variable chips stay pinned to the bottom regardless of how long the message is.uses is free text, so it can carry a period as well as a count without the block needing to format anything.button elements with aria-pressed and explicit cursor-pointer, since Tailwind v4 buttons default to cursor: default.chat31
A messaging section splitting a hairline matrix between the staff view and the member view, each half floating its own live asset above a title and a list of what that side gets.
chat23
A messaging section pairing a light heading, a hairline table of response figures, and two actions with an image tile that floats a live conversation thread.
chat12
Chat message thread with rich formatting, copy and thumbs-up/down action buttons on AI messages, and time-grouped message sections. Features hover-revealed action bars and smooth animations.
chat6
Perplexity-style AI response with inline numbered citation markers, a source panel showing referenced URLs with favicons and titles, and expandable source previews. Perfect for RAG-powered chat applications.