shadcn.io is not affiliated with official shadcn/ui
Bubble Style Chat Block for React
A messenger thread that shows how bubbles group: the first, middle and last bubble of a run have different corners, the last has a square tail corner, and sending messages in a row joins them live.
Installation
Overview
Show how a run of messages is drawn, with this bubble style chat for React with Base UI. A hairline frame with a plus mark on each corner holds a window of fixed height for Pearlwick, a messenger: a thread with Aveline Marchetti built from Message and Bubble rows, where the bubbles of one sender's run have tight inner corners and the last bubble's outer-bottom corner is square, its tail, with her initials and the time under it. A run breaks after five minutes of silence. The top bar counts the runs, and sending messages in a row really joins them: the bubble that was last loses its tail and its time as the new one lands, and the line under the composer says whether your message joined a run or began one. The composer's Shine Button, the only colored control, sends, and Aveline answers after you stop, behind a Typing Indicator Text. The frame is ruled by Guides, and everything moves with CSS only. For a messenger, a chat widget or any thread where the shape of a bubble carries meaning.
FAQ
How do the corners and tails work?
A pure function, runsOf, marks each message as first or last of its run, and the bubble's corner classes come from that: the inner corners beside the other side of the thread are tight on a run's first, middle and last bubbles, and the last bubble's outer-bottom corner is square. It is a logical corner (rounded-ee-sm for yours, rounded-es-sm for theirs), so it flips in right-to-left languages, and it is the bubble's own border radius, never a drawn triangle.
What really joins when I send a few in a row?
Messages from one sender within five minutes of each other form a run. Send three quickly and each new bubble becomes the last of the run, so the earlier one loses its tail and its time; the count of runs in the top bar stays put. Her reply waits for you to stop (each send restarts the wait), so the run is not broken between your messages.
What stays in the page, and how do I make it real?
Everything. Times are typed in the list, and the replies come from a short typed script, as the status line says; nothing is sent and no model runs. To wire it for real, send each message to your server where the code says so and append what arrives to the same list: the grouping, tails and times follow from the data.
How does the composer work?
Enter sends and Shift and Enter add a line, which the field's description says for a screen reader; Enter during text composition is left alone. The field grows with its content up to a limit, Send stays quiet while it is empty and says so if pressed, and focus stays in the field after sending.
Is it usable with a keyboard and a screen reader?
The log is a focusable region announced as a log, so new messages are read out, and every message starts with a hidden "Aveline Marchetti, 09:12" or "You, 09:15" because the avatar is decorative. Nothing relies on color: the tail is shape and the run count is text.