About
The ChatBubble component chat-bubble.tsx is the main component in this library. It is used to display a chat bubble along with its message, avatar and other content. Wrap the ChatBubble component with ChatMessageList to properly show the chat bubbles.
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Chat Bubble to your project and fits it to your theme.
Code
How Chat Bubble is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
import {
ChatBubble,
ChatBubbleAvatar,
ChatBubbleMessage,
ChatBubbleAction
} from "@/components/ui/chat-bubble"
import { Copy, RefreshCcw } from "lucide-react"
const messages = [
{
id: 1,
message: "Help me with my essay.",
sender: "user",
},
{
id: 2,
message: "I can help you with that. What do you need help with?",
sender: "bot",
},
]
const actionIcons = [
{ icon: Copy, type: "Copy" },
{ icon: RefreshCcw, type: "Regenerate" },
]
export function ChatBubbleVariants() {
return (
<div className="max-w-md space-y-4 p-4">
<ChatBubble variant="sent">
<ChatBubbleAvatar fallback="US" src="https://cdn.21st.dev/assets/mirror/4d/4db029bb98244eb14fbadba571c02ba446b82164dde3776ea3a7330e8f88f190.jpg" />
<ChatBubbleMessage variant="sent">
I have a question about the library.
</ChatBubbleMessage>
</ChatBubble>
<ChatBubble variant="received">
<ChatBubbleAvatar fallback="AI" src="https://cdn.21st.dev/assets/mirror/14/145a595e9641b63540835840271b593d4f3f0e757f1cf3a351bca4b88321a99b.jpg" />
<ChatBubbleMessage>
Sure, I'd be happy to help!
</ChatBubbleMessage>
</ChatBubble>
</div>
)
}
export function ChatBubbleAiLayout() {
return (
<div className="max-w-md divide-y">
{messages.map((message, index) => {
const variant = message.sender === "user" ? "sent" : "received"
return (
<div key={message.id} className="py-6 first:pt-0 last:pb-0">
<div className="flex gap-3">
<ChatBubbleAvatar
src={variant === "sent"
? "https://cdn.21st.dev/assets/mirror/4d/4db029bb98244eb14fbadba571c02ba446b82164dde3776ea3a7330e8f88f190.jpg"
: "https://cdn.21st.dev/assets/mirror/14/145a595e9641b63540835840271b593d4f3f0e757f1cf3a351bca4b88321a99b.jpg"
}
fallback={variant === "sent" ? "US" : "AI"}
/>
<div className="flex-1">
{message.message}
{message.sender === "bot" && (
<div className="flex gap-2 mt-2">
{actionIcons.map(({ icon: Icon, type }) => (
<button
key={type}
onClick={() => console.log(`Action ${type} clicked for message ${index}`)}
className="p-1 hover:bg-muted rounded-md transition-colors"
>
<Icon className="size-3" />
</button>
))}
</div>
)}
</div>
</div>
</div>
)
})}
</div>
)
}
export function ChatBubbleStates() {
return (
<div className="max-w-md space-y-4 p-4">
<ChatBubble variant="received">
<ChatBubbleAvatar fallback="AI" />
<ChatBubbleMessage isLoading />
</ChatBubble>
<ChatBubble variant="received">
<ChatBubbleAvatar fallback="AI" />
<ChatBubbleMessage className="bg-destructive/10 text-destructive">
Error processing request
</ChatBubbleMessage>
</ChatBubble>
</div>
)
}













