Blode UI
GitHub

Chat Message

A chat transcript entry that styles user and assistant messages, with attachments and a hover-revealed meta row.

Loading
Loading...
import {
  ArrowRotateCounterClockwiseIcon,
  PencilIcon,
  SquareBehindSquare1Icon,
} from "blode-icons-react";
 
import { Button } from "@/components/ui/button";
import { ChatMessage } from "@/components/ui/chat-message";
 
// Icon-only action buttons for the hover-revealed meta row. Assistant replies
// get copy + regenerate; user messages get copy + edit. Illustrative only —
// the buttons carry no behaviour in this demo.
function MessageActions({ from }: { from: "user" | "assistant" }) {
  return (
    <>
      <Button aria-label="Copy message" size="icon-xs" variant="ghost">
        <SquareBehindSquare1Icon />
      </Button>
      {from === "user" ? (
        <Button aria-label="Edit message" size="icon-xs" variant="ghost">
          <PencilIcon />
        </Button>
      ) : (
        <Button aria-label="Regenerate response" size="icon-xs" variant="ghost">
          <ArrowRotateCounterClockwiseIcon />
        </Button>
      )}
    </>
  );
}
 
export function ChatMessageDemo() {
  return (
    <div className="flex w-full max-w-xl flex-col gap-2">
      <ChatMessage actions={<MessageActions from="user" />} from="user" time="Wednesday 6:06 PM">
        What does &ldquo;good design&rdquo; actually mean? Everyone says it, no one defines it.
      </ChatMessage>
      <ChatMessage actions={<MessageActions from="assistant" />} from="assistant">
        Good design is mostly invisible — you only notice it when it&apos;s missing. It&apos;s less
        about how something looks and more about how effortlessly it lets you do what you came to
        do.
      </ChatMessage>
      <ChatMessage actions={<MessageActions from="user" />} from="user" time="Wednesday 6:07 PM">
        So function over form?
      </ChatMessage>
      <ChatMessage actions={<MessageActions from="assistant" />} from="assistant">
        Not quite. Form is part of function — something that feels good to use is, in a real sense,
        working better. The split between the two is mostly a myth.
      </ChatMessage>
      <ChatMessage actions={<MessageActions from="user" />} from="user" time="Wednesday 6:08 PM">
        That reframes it completely.
      </ChatMessage>
    </div>
  );
}

Installation

npx shadcn@latest add "https://ui.blode.co/r/styles/default/chat-message"

Usage

import { ChatMessage } from "@/components/ui/chat-message";
<ChatMessage from="user" time="Wednesday 6:08 PM">
  Can you summarize the Q3 report?
</ChatMessage>
<ChatMessage from="assistant">Revenue grew 14% quarter-over-quarter.</ChatMessage>

Examples

With attachments

Pass File objects to files to render attachment thumbnails above the message. Images show a preview; other files show an icon and name.

Loading
Loading...
import { ChatMessage } from "@/components/ui/chat-message";
 
export function ChatMessageWithAttachments() {
  const files = [
    new File(["%PDF-1.4"], "design-brief.pdf", { type: "application/pdf" }),
    new File(["notes"], "research-notes.txt", { type: "text/plain" }),
  ];
 
  return (
    <div className="flex w-full max-w-md flex-col gap-3">
      <ChatMessage files={files} from="user">
        Here are the files for review.
      </ChatMessage>
      <ChatMessage from="assistant">
        Got them — I&apos;ll go through the brief and pull out the open questions.
      </ChatMessage>
    </div>
  );
}