Chat
Message.
Layout for one chat message: avatar, header, content and footer, aligned to the start or end of the thread.
import { Check, Cpu } from "lucide-react";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Bubble, BubbleContent, BubbleGroup } from "@/components/ui/bubble";
import {
Message,
MessageAvatar,
MessageContent,
MessageFooter,
MessageGroup,
MessageHeader,
} from "@/components/ui/message";
export default function MessageDemo() {
return (
<MessageGroup className="w-full max-w-md gap-5">
<Message>
<MessageAvatar>
<Avatar>
<AvatarFallback>
<Cpu className="size-4" />
</AvatarFallback>
</Avatar>
</MessageAvatar>
<MessageContent>
<MessageHeader>
<span className="text-success">Hub terminal</span>
</MessageHeader>
<BubbleGroup>
<Bubble variant="muted">
<BubbleContent>Turtle T-03 finished the north tunnel.</BubbleContent>
</Bubble>
<Bubble variant="muted">
<BubbleContent>Fuel is at 12 percent. Send it home?</BubbleContent>
</Bubble>
</BubbleGroup>
<MessageFooter>
<time dateTime="2026-09-22T09:41">09:41</time>
</MessageFooter>
</MessageContent>
</Message>
<Message align="end">
<MessageContent>
<Bubble>
<BubbleContent>Yes, park it at the charging dock.</BubbleContent>
</Bubble>
<MessageFooter>
<time dateTime="2026-09-22T09:42">09:42</time>
<Check className="text-primary" aria-label="Read" />
</MessageFooter>
</MessageContent>
</Message>
</MessageGroup>
);
}Installation#
With the CLI
$npx lanterncn add messageThis also installs the Lantern theme and any Lantern UI components it depends on. The shadcn CLI works directly too: npx shadcn@latest add httptim/lantern-ui/message. See Installation if your project is not set up yet.
Manually
Copy the source into your project
components/ui/message.tsximport * as React from "react"; import { cn } from "@/lib/utils"; function MessageGroup({ className, ...props }: React.ComponentProps<"div">) { return <div data-slot="message-group" className={cn("flex min-w-0 flex-col gap-2", className)} {...props} />; } function Message({ className, align = "start", ...props }: React.ComponentProps<"div"> & { align?: "start" | "end" }) { return ( <div data-slot="message" data-align={align} className={cn( "group/message relative flex w-full min-w-0 gap-2.5 text-sm data-[align=end]:flex-row-reverse", className, )} {...props} /> ); } function MessageAvatar({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="message-avatar" className={cn( "flex w-fit min-w-8 shrink-0 items-center justify-center self-end overflow-hidden rounded-full bg-secondary group-has-data-[slot=message-footer]/message:-translate-y-6 [&_[data-slot=avatar]]:size-8", className, )} {...props} /> ); } function MessageContent({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="message-content" className={cn( "flex w-full min-w-0 flex-col gap-1.5 wrap-break-word group-data-[align=end]/message:*:data-slot:self-end", className, )} {...props} /> ); } function MessageHeader({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="message-header" className={cn( "flex max-w-full min-w-0 items-center gap-2 px-1 font-mono text-[10px] tracking-[0.15em] text-muted-foreground uppercase group-has-data-[variant=ghost]/message:px-0", className, )} {...props} /> ); } function MessageFooter({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="message-footer" className={cn( "flex max-w-full min-w-0 items-center gap-2 px-1 font-mono text-[10px] tracking-[0.1em] text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end [&_svg:not([class*='size-'])]:size-3", className, )} {...props} /> ); } export { MessageGroup, Message, MessageAvatar, MessageContent, MessageFooter, MessageHeader };Update the import paths to match your project
The source imports
cnfrom@/lib/utils.
Usage#
import {
MessageGroup,
Message,
MessageAvatar,
MessageContent,
MessageFooter,
MessageHeader,
} from "@/components/ui/message";<Message align="end">
<MessageContent>
<Bubble>
<BubbleContent>Park it at the charging dock.</BubbleContent>
</Bubble>
<MessageFooter>09:42</MessageFooter>
</MessageContent>
</Message>Message only handles layout. Put Bubble, Attachment or any other content inside MessageContent; with align="end" its children line up on the right.