Message
A single chat message from the person or the assistant, in three layouts.
Loading preview…
Install
- 1
Run this command in your Tailwind CSS project. It adds
components/ai/message-bubbles.tsxand installs its dependencies.$ npx shadcn@latest add https://gradientdeck.com/ai-components/r/message-bubbles.json
Usage & API
Example
import { MessageBubble } from "@/components/ai/message-bubbles"
<MessageBubble
role="user"
children={children}
/>Props MessageBubble
| Prop | Type & description | Required |
|---|---|---|
role | "user" | "assistant" | Required |
children | ReactNode | Required |
meta | stringShown under the bubble, e.g. a time or "Edited". | Optional |
Dependencies
- React
- Tailwind CSS
- motion
Implementation notes
Bubbles exports MessageBubble. role is user or assistant. The entrance is a short rise and fade. The user bubble uses the sand tone and dark text. The assistant bubble uses a white or zinc surface, a border, and a light shadow. There is no name prop and no avatar image. The orb is decorative and aria-hidden. meta is optional and unstyled beyond size and color, so pass a short string. The typing indicator in the demo is a sibling: a status labelled Assistant is typing, the same orb, and three dots delayed by 0.15 seconds. The demo script shows a user turn, an assistant turn, then a second user turn, the typing row, then the second assistant turn. Copy the sequence if you want that pause. Do not look for a typing prop on MessageBubble.
Example
import { MessageDocument } from "@/components/ai/message-document"
<MessageDocument
role="user"
children={children}
/>Props MessageDocument
| Prop | Type & description | Required |
|---|---|---|
role | "user" | "assistant" | Required |
children | ReactNode | Required |
name | string | Optional |
Dependencies
- React
- Tailwind CSS
- motion
Implementation notes
Document exports MessageDocument. The user block has a light fill and, when name is passed, a label above the body. The assistant article does not use name. Its class list styles code with a small rounded background, h4 as a semibold title, ordered lists with decimals, and strong in a darker zinc. You still write the tags. The component will not turn a markdown string into those tags. Pair it with Streaming text's markdown variant if the text arrives as a string and has to look finished mid-stream, then drop the finished tree in as children. The entrance delay on the assistant article is 0.08 seconds, so a prompt and its answer do not pop as one block.
Example
import { MessageCompact } from "@/components/ai/message-compact"
<MessageCompact
author={author}
time="…"
children={children}
/>Props MessageCompact
| Prop | Type & description | Required |
|---|---|---|
author | CompactAuthor | Required |
time | string | Required |
children | ReactNode | Required |
continued | booleanHide the avatar and name when the same author posts twice in a row. | Optional |
Dependencies
- React
- Tailwind CSS
- motion
Implementation notes
Compact exports MessageCompact and the CompactAuthor type. The row is a horizontal flex. The avatar column is fixed. A continued row leaves that column for a hover time, the first token of time, hidden until the group is hovered. The header line is the name, an AI chip when kind is assistant, and a time element. User avatars do not get the chip. color defaults to a neutral zinc if you omit it. The demo uses a pink for Nora and a blue for Dev. Children are 13.5 pixel text. The channel label # launch-checklist is outside the component, in the demo frame. A later message fades in when the demo's timer fires. There is no grouping helper. You pass continued.
Choosing a layout
Use Bubbles for a two-party chat where the person's turns and the assistant's turns should be obvious at a glance. Meta is the right place for 9:41 or for Edited after Edit message saves. Use Document when the assistant answer is something to read, with steps and code, and the person's prompt should look like a quoted brief rather than a pill. The demo asks about a slow Postgres query and answers with an ordered list and code for EXPLAIN ANALYZE and VACUUM ANALYZE. Use Compact when several people and an assistant share one log. The demo channel is labelled # launch-checklist. The assistant name gets an AI badge. A user avatar is the first character of the name on the color you pass.
Streaming text belongs inside the children, or in place of them, while tokens are still arriving. Message does not take a shown count or a caret. Message actions sits under an assistant turn. It is not built into these layouts. Branch picker is how you step between alternate versions of one reply. The compact continued flag is your job. Compare the previous author id, and pass continued so the second bubble does not show a second avatar. The demo does that for the assistant's follow-up question about opening a pull request.
Can I pass a markdown string as children and get headings?
No. children is a React node, rendered as is. Document will style h4, ol, code and strong if you pass those elements, which is what the Postgres demo does. A raw string stays a paragraph. For a string that is still streaming, use Streaming text, whose markdown variant parses headings, lists and fences while the source is incomplete. When the stream finishes, you can keep that view or replace it with Message.
How does a second assistant paragraph avoid a second avatar in the compact log?
Pass continued. The component then skips the avatar, the name and the AI badge, and only shows the time in the gutter while the row is hovered. It does not compare authors. If you forget the flag, every call renders a full header, even when the name matches the previous row. The time string is also what the hover gutter slices. 10:02 AM shows 10:02 there, because it splits on the space.