For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. An agent skill is available at /.well-known/agent-skills/site-skill.md.
Home

Message

A layout wrapper for a chat message row with avatar, header, content, and footer.

Today
Can you explain how the GIN Design component system works?
Delivered · 14:30
Assistant · 14:31
GIN Design is a shadcn-based registry. Each component lives in registry/mint/ and can be installed via the CLI.
It supports multiple themes — mint, berries, citrine, and cinnamon — each with its own token overrides.
Thinking…
import { BotIcon, CopyIcon, ThumbsDownIcon, ThumbsUpIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Bubble, BubbleContent } from "@/components/ui/bubble";
import { Marker, MarkerContent } from "@/components/ui/marker";
import { Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader } from "@/components/ui/message";

export const MessageDemo = () => (
    <div className="flex flex-col gap-4">
      <Marker variant="separator"><MarkerContent>Today</MarkerContent></Marker>
      {/* User message */}
      <Message align="end">
        <MessageContent>
          <Bubble variant="default" align="end"><BubbleContent>Can you explain how the GIN Design component system works?</BubbleContent></Bubble>
          <MessageFooter>Delivered · 14:30</MessageFooter>
        </MessageContent>
      </Message>
      {/* Assistant message group */}
      <MessageGroup>
        <Message align="start">
          <MessageAvatar><div className="flex size-8 items-center justify-center rounded-full bg-primary text-primary-foreground"><BotIcon className="size-4" /></div></MessageAvatar>
          <MessageContent>
            <MessageHeader>Assistant · 14:31</MessageHeader>
            <Bubble variant="secondary"><BubbleContent>GIN Design is a shadcn-based registry. Each component lives in{" "}<code>registry/mint/</code> and can be installed via the CLI.</BubbleContent></Bubble>
            <Bubble variant="secondary"><BubbleContent>It supports multiple themes — mint, berries, citrine, and cinnamon — each with its own token overrides.</BubbleContent></Bubble>
            <MessageFooter>
              <Button variant="ghost" size="icon-xs" aria-label="Copy"><CopyIcon /></Button>
              <Button variant="ghost" size="icon-xs" aria-label="Helpful"><ThumbsUpIcon /></Button>
              <Button variant="ghost" size="icon-xs" aria-label="Not helpful"><ThumbsDownIcon /></Button>
            </MessageFooter>
          </MessageContent>
        </Message>
      </MessageGroup>
      {/* AI reasoning marker */}
      <Marker><MarkerContent className="shimmer">Thinking…</MarkerContent></Marker>
    </div>
  );

Alignment

Set align on the root Message to switch between incoming and outgoing layouts. All sub-components adapt automatically.

<Message align="start">
  <MessageContent>
    <Bubble variant="secondary"><BubbleContent>Hello!</BubbleContent></Bubble>
  </MessageContent>
</Message>
 
<Message align="end">
  <MessageContent>
    <Bubble variant="default"><BubbleContent>Hi there!</BubbleContent></Bubble>
  </MessageContent>
</Message>

With avatar

MessageAvatar anchors to the bottom of the message row. It lifts up automatically when a MessageFooter is present.

<Message align="start">
  <MessageAvatar>
    <Avatar>
      <AvatarImage src="/avatars/ai.png" alt="AI" />
      <AvatarFallback>AI</AvatarFallback>
    </Avatar>
  </MessageAvatar>
  <MessageContent>
    <Bubble variant="secondary"><BubbleContent>How can I help?</BubbleContent></Bubble>
  </MessageContent>
</Message>

Grouping

Wrap consecutive messages from the same sender in MessageGroup to suppress redundant avatars and reduce spacing.

<MessageGroup>
  <Message align="start">
    <MessageAvatar>...</MessageAvatar>
    <MessageContent>
      <Bubble variant="secondary"><BubbleContent>First</BubbleContent></Bubble>
    </MessageContent>
  </Message>
  <Message align="start">
    <MessageContent>
      <Bubble variant="secondary"><BubbleContent>Second</BubbleContent></Bubble>
    </MessageContent>
  </Message>
</MessageGroup>

MessageHeader and MessageFooter add sender name / timestamp rows above and below the message content.

<Message align="start">
  <MessageAvatar>...</MessageAvatar>
  <div>
    <MessageHeader>
      <span className="font-medium text-sm">Claude</span>
    </MessageHeader>
    <MessageContent>
      <Bubble variant="secondary"><BubbleContent>Here is the summary.</BubbleContent></Bubble>
    </MessageContent>
    <MessageFooter>
      <span className="text-xs text-muted-foreground">12:04 PM</span>
    </MessageFooter>
  </div>
</Message>

With Bubble

Typical full composition pairing Message, Bubble, and BubbleContent.

import { Message, MessageAvatar, MessageContent } from "@/components/ui/message";
import { Bubble, BubbleContent } from "@/components/ui/bubble";
 
<Message align="start">
  <MessageAvatar>
    <Avatar><AvatarFallback>AI</AvatarFallback></Avatar>
  </MessageAvatar>
  <MessageContent>
    <Bubble variant="ghost">
      <BubbleContent>
        Here is everything you need to know about the topic.
      </BubbleContent>
    </Bubble>
  </MessageContent>
</Message>

Props

All Message sub-components are layout primitives. They accept className and forward all standard HTML div attributes.

Message

PropTypeDefaultDescription
align"start" | "end""start"Aligns the row left (incoming) or right (outgoing)
classNamestring—Additional CSS classes

MessageAvatar

PropTypeDefaultDescription
classNamestring—Additional CSS classes

MessageHeader

PropTypeDefaultDescription
classNamestring—Additional CSS classes

MessageContent

PropTypeDefaultDescription
classNamestring—Additional CSS classes

MessageFooter

PropTypeDefaultDescription
classNamestring—Additional CSS classes

Usage

import {
  Message,
  MessageAvatar,
  MessageContent,
  MessageFooter,
  MessageGroup,
  MessageHeader,
} from "@/components/ui/message";
import { Bubble, BubbleContent } from "@/components/ui/bubble";
 
<Message align="start">
  <MessageAvatar>
    <Avatar><AvatarFallback>AI</AvatarFallback></Avatar>
  </MessageAvatar>
  <MessageContent>
    <Bubble variant="secondary">
      <BubbleContent>Hello! How can I help?</BubbleContent>
    </Bubble>
  </MessageContent>
</Message>

Specs

# Mint — Message Specs
 
## Component
- name: message
- file: registry/mint/message.tsx
- installs to: components/ui/message.tsx
- dependencies: none
- registryDependencies: none
 
## What is it
A pure layout primitive for a single chat message row. Provides the spatial
structure (avatar column + content column) and alignment context for chat UIs.
Has no visual styling of its own — all colour, shape, and surface come from
Bubble or custom children. Do not use it for non-chat content; prefer Card or
a custom layout instead.
 
## Visual identity
Invisible by itself — no background, no border, no radius. Visual weight is
entirely delegated to the Bubble component placed inside MessageContent. The
avatar slot uses --muted as a fallback background for the rounded container.
 
## Design tokens
--muted: oklch(0.96 0.02 155) / oklch(0.2 0.03 155)           — MessageAvatar fallback background
--muted-foreground: oklch(0.52 0.06 155) / oklch(0.6 0.06 155) — MessageHeader and MessageFooter text colour
 
## Sub-components
- MessageGroup    → flex flex-col gap-2; wraps consecutive same-sender messages to reduce spacing
- Message         → full-width flex row; drives alignment via data-align; base text-sm
- MessageAvatar   → shrink-0 self-end rounded-full overflow-hidden bg-muted; auto-offsets when footer present
- MessageContent  → full-width flex column gap-2.5; forces wrap-break-word; aligns children self-end when align="end"
- MessageHeader   → px-3 text-xs font-medium text-muted-foreground; px-0 when inside ghost Bubble
- MessageFooter   → px-3 text-xs font-medium text-muted-foreground; justify-end when align="end"; px-0 when ghost
 
## States
- data-align=end:   reverses the flex row (avatar right, content left) on Message
- group-has-data-[slot=message-footer]: MessageAvatar translates up by 2rem to stay level with content bottom
- group-has-data-[variant=ghost]:       MessageHeader and MessageFooter drop horizontal padding (px-0)
- group-data-[align=end]:               MessageContent direct children with data-slot align self-end; MessageFooter justify-end
 
## Conventions
- Always place a Bubble (or equivalent surface) inside MessageContent — Message is layout-only, not a surface
- Use MessageGroup to suppress repeated avatars and compress spacing between consecutive turns from the same sender
- MessageHeader typically carries sender name; MessageFooter typically carries a timestamp or read receipt
- Do not add border, background, or padding directly to Message — it is layout-only
- Sub-components expose data-slot="message-group|message|message-avatar|message-content|message-header|message-footer" for CSS targeting
- The group/message group class on Message enables child components to query parent state via Tailwind group variants — do not rename or remove it

Installation

GIN Design Plugin
$ npx shadcn add https://gin-design.vercel.app/message