Message
Displays a message in a conversation, with optional avatar, header, footer, and alignment.
Preview
Loading...
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/Avatar";
import { Bubble, BubbleContent } from "@/components/ui/Bubble";
import { Button } from "@/components/ui/Button";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/Marker";
import {
Message,
MessageAvatar,
MessageContent,
MessageFooter,
MessageGroup,
MessageHeader,
} from "@/components/ui/Message";
import { SymbolIcon } from "@radix-ui/react-icons";
export function MessageDemo() {
return (
<div className="flex w-full max-w-lg flex-col gap-2">
<Message>
<MessageAvatar>
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</MessageAvatar>
<MessageContent>
<Bubble variant="secondary">
<BubbleContent>It's 4:55 PM. On a Friday.</BubbleContent>
</Bubble>
</MessageContent>
</Message>
<Message align="end">
<MessageContent>
<Bubble>
<BubbleContent>How can I help you today?</BubbleContent>
</Bubble>
<MessageFooter>Delivered</MessageFooter>
</MessageContent>
</Message>
<Message>
<Marker role="status">
<MarkerIcon>
<SymbolIcon className="icon animate-spin" />
</MarkerIcon>
<MarkerContent>Oliver is typing...</MarkerContent>
</Marker>
</Message>
</div>
)
}Installation
Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces
pnpm dlx shadcn@latest add @aura/messageManual
Copy and paste the class names utility into your utils/class-names.ts file.
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Copy and paste the Message component into your components/ui/Message.tsx file.
/**
* @description Displays a message in a conversation, with optional avatar, header, footer, and alignment.
*/
import * as React from "react";
import { cn } from "@/utils/class-names";
function MessageGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="message-group"
className={cn("flex min-w-0 flex-col gap-1", 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-0.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-3 shrink-0 items-center justify-center self-end overflow-hidden rounded-full bg-gray-3 group-has-data-[slot=message-footer]/message:-translate-y-2",
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 break-words 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 px-1 text-xs font-medium text-gray-11 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 px-1 text-xs font-medium text-gray-11 group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end",
className
)}
{...props}
/>
);
}
export {
MessageGroup,
Message,
MessageAvatar,
MessageContent,
MessageFooter,
MessageHeader,
};
Usage
Alignment
export const Alignment = () => (
<div className="flex w-full max-w-lg flex-col gap-2">
<Message>
<MessageAvatar>
<Avatar>
<AvatarFallback>AI</AvatarFallback>
</Avatar>
</MessageAvatar>
<MessageContent>
<Bubble variant="secondary">
<BubbleContent>The build failed during dependency installation.</BubbleContent>
</Bubble>
</MessageContent>
</Message>
<Message align="end">
<MessageContent>
<Bubble>
<BubbleContent>Can you share the exact error?</BubbleContent>
</Bubble>
</MessageContent>
</Message>
<Message>
<MessageAvatar>
<Avatar>
<AvatarFallback>AI</AvatarFallback>
</Avatar>
</MessageAvatar>
<MessageContent>
<Bubble variant="secondary">
<BubbleContent>
Something went wrong with the build. Try running it again.
</BubbleContent>
</Bubble>
</MessageContent>
</Message>
</div>
)Group
export const Group = () => (
<div className="flex w-full max-w-lg flex-col gap-2">
<MessageGroup>
<Message>
<MessageAvatar className="invisible" aria-hidden />
<MessageContent>
<Bubble variant="secondary">
<BubbleContent>I checked the registry addresses.</BubbleContent>
</Bubble>
</MessageContent>
</Message>
<Message>
<MessageAvatar>
<Avatar>
<AvatarFallback>AI</AvatarFallback>
</Avatar>
</MessageAvatar>
<MessageContent>
<Bubble variant="secondary">
<BubbleContent>They look correct on my end.</BubbleContent>
</Bubble>
</MessageContent>
</Message>
</MessageGroup>
</div>
)HeaderAndFooter
export const HeaderAndFooter = () => (
<div className="flex w-full max-w-lg flex-col gap-2">
<Message>
<MessageAvatar>
<Avatar>
<AvatarFallback>YO</AvatarFallback>
</Avatar>
</MessageAvatar>
<MessageContent>
<MessageHeader>You</MessageHeader>
<Bubble variant="secondary">
<BubbleContent>
Send the report to the team. Ping @shadcn if you need help.
</BubbleContent>
</Bubble>
<MessageFooter>Just now</MessageFooter>
</MessageContent>
</Message>
</div>
)Actions
export const Actions = () => (
<div className="flex w-full max-w-lg flex-col gap-2">
<Message>
<MessageContent>
<Bubble variant="ghost">
<BubbleContent>
The install failure is coming from the workspace package.
</BubbleContent>
</Bubble>
<MessageFooter className="gap-0.5">
<Button variant="pill" size="xs" type="button" aria-label="Copy">
Copy
</Button>
<Button variant="pill" size="xs" type="button" aria-label="Retry">
Retry
</Button>
</MessageFooter>
</MessageContent>
</Message>
</div>
)