Sabil UI

Search documentation

Search for a command to run...

Components

Message

Layout for a single message in a conversation, with avatar, header and footer.

ME
Deploying to prod real quick.
R
It's 4:55 PM. On a Friday.
ME
It's a one-line change.
Delivered
R
It's always a one-line change ๐Ÿ˜ญ.
Alright, let me take a look.
๐Ÿ‘
Oliver is typing...

Installation#

  1. Install the dependencies

    npm install cn class-variance-authority radix-ui
  2. Copy the source into your project

    components/ui/message.tsx
    import * as React from "react"import { cn } from "cn"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 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-muted group-has-data-[slot=message-footer]/message:-translate-y-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-2.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 px-3 text-xs font-medium text-muted-foreground 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-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end",        className      )}      {...props}    />  )}export {  MessageGroup,  Message,  MessageAvatar,  MessageContent,  MessageFooter,  MessageHeader,}
    components/ui/bubble.tsx
    import * as React from "react"import { cva, type VariantProps } from "class-variance-authority"import { cn } from "cn"import { Slot } from "radix-ui"function BubbleGroup({ className, ...props }: React.ComponentProps<"div">) {  return (    <div      data-slot="bubble-group"      className={cn("flex min-w-0 flex-col gap-2", className)}      {...props}    />  )}const bubbleVariants = cva(  "group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1 group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full",  {    variants: {      variant: {        default:          "*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/80",        secondary:          "*:data-[slot=bubble-content]:bg-bubble-strong *:data-[slot=bubble-content]:text-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklab,var(--bubble-strong),var(--foreground)_6%)]",        muted:          "*:data-[slot=bubble-content]:bg-bubble *:data-[slot=bubble-content]:text-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-bubble-strong",        tinted:          "*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.93_calc(c*0.4)_h)] *:data-[slot=bubble-content]:text-foreground dark:*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.3_calc(c*0.4)_h)] [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.88_calc(c*0.5)_h)] dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.35_calc(c*0.5)_h)]",        outline:          "*:data-[slot=bubble-content]:border-border *:data-[slot=bubble-content]:bg-background [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-input/30",        ghost:          "border-none *:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/50",        destructive:          "*:data-[slot=bubble-content]:bg-destructive/10 *:data-[slot=bubble-content]:text-destructive dark:*:data-[slot=bubble-content]:bg-destructive/20 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/20 dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/30",      },    },    defaultVariants: {      variant: "default",    },  })function Bubble({  variant = "default",  align = "start",  className,  ...props}: React.ComponentProps<"div"> &  VariantProps<typeof bubbleVariants> & {    align?: "start" | "end"  }) {  return (    <div      data-slot="bubble"      data-variant={variant}      data-align={align}      className={cn(bubbleVariants({ variant }), className)}      {...props}    />  )}function BubbleContent({  asChild = false,  className,  ...props}: React.ComponentProps<"div"> & {  asChild?: boolean}) {  const Comp = asChild ? Slot.Root : "div"  return (    <Comp      data-slot="bubble-content"      className={cn(        "w-fit max-w-full min-w-0 overflow-hidden rounded-xl border border-transparent px-3 py-2 text-sm leading-relaxed wrap-break-word group-data-[align=end]/bubble:self-end [button]:text-left [button,a]:transition-colors [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-3 [button,a]:focus-visible:ring-ring/50",        className      )}      {...props}    />  )}const bubbleReactionsVariants = cva(  "absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-sm ring-3 ring-card has-[button]:p-0",  {    variants: {      side: {        top: "top-0 -translate-y-3/4",        bottom: "bottom-0 translate-y-3/4",      },      align: {        start: "left-3",        end: "right-3",      },    },    defaultVariants: {      side: "bottom",      align: "end",    },  })function BubbleReactions({  side = "bottom",  align = "end",  className,  ...props}: React.ComponentProps<"div"> & {  align?: "start" | "end"  side?: "top" | "bottom"}) {  return (    <div      data-slot="bubble-reactions"      data-align={align}      data-side={side}      className={cn(bubbleReactionsVariants({ side, align }), className)}      {...props}    />  )}export { BubbleGroup, Bubble, BubbleContent, BubbleReactions }
    
  3. Make sure the foundation is in place

    Components read their colors, radius and surfaces from the tokens in app/globals.css. See Theming.

Usage#

import {  MessageGroup,  Message,  MessageAvatar,  MessageContent,  MessageFooter,  MessageHeader,} from "@/components/ui/message"

Examples#

Actions

The install failure is coming from the workspace package.
Okay drop me a link. Taking a look...
Failed to send

Attachment

Workspace
Here's the image. Can you add it to the PDF? Use it for the cover page.
Done. Here's the PDF with the image added as the cover page.
sales-dashboard.pdfPDF ยท 2.4 MB
Thanks. Looks good.

Avatar

R
The build failed during dependency installation.
R
Can you share the exact error?
R
Here's the error from the logs
Something went wrong with the build. The libraries are not installed correctly. Try running the build again.

Group

I checked the registry addresses.
CN
The component and example JSON now live under the UI registry.
Olivia
I already checked the logs.
Send the report to the team. Ping @maya if you need help.
Read Yesterday