ChatMessage

PreviousNext

A single chat bubble that renders differently based on the sender — left-aligned for the assistant, right-aligned with a background for the user.

Hi. Ask me anything about Club Med. I'm here to help you find the right information quickly.

My son is 14 years old, and I would like to find activities for him during our stay. What do you suggest?

'use client';

import { ChatMessage } from '@/ui/ChatMessage';

export function ChatMessageDemo() {
  return (
    <div className="w-[380px] flex flex-col gap-4">
      <ChatMessage sender="assistant">
        Hi. Ask me anything about Club Med. I&apos;m here to help you find the right information
        quickly.
      </ChatMessage>
      <ChatMessage sender="user">
        My son is 14 years old, and I would like to find activities for him during our stay. What do
        you suggest?
      </ChatMessage>
    </div>
  );
}

Installation

npx shadcn@latest add https://develop.trident-ui.pro.clubmed/r/chat-message.json

Usage

import { ChatMessage } from '@/ui/ChatMessage';
 
export function Component() {
  return (
    <>
      <ChatMessage sender="assistant">Hi. Ask me anything about Club Med.</ChatMessage>
      <ChatMessage sender="user">What activities are available for children?</ChatMessage>
    </>
  );
}

Actions

Give a user message onReplay, onEdit and/or onCopy to show its actions under the bubble. They are revealed when the bubble is hovered or focused (always visible on devices without hover), and a button is only rendered when its callback is provided. They are ignored for assistant messages.

Hi. Ask me anything about Club Med. I'm here to help you find the right information quickly.

Can you suggest activities for a 14 year old?

'use client';

import { useState } from 'react';
import { ChatMessage } from '@/ui/ChatMessage';

export function ChatMessageWithActionsDemo() {
  const [content, setContent] = useState('Can you suggest activities for a 14 year old?');

  return (
    <div className="w-[380px] flex flex-col gap-4">
      <ChatMessage sender="assistant">
        Hi. Ask me anything about Club Med. I&apos;m here to help you find the right information
        quickly.
      </ChatMessage>
      <ChatMessage
        sender="user"
        onReplay={() => {}}
        onEdit={setContent}
        onCopy={() => navigator.clipboard.writeText(content)}
      >
        {content}
      </ChatMessage>
    </div>
  );
}

Copy confirmation

Once onCopy succeeded, the copy icon is replaced by a check for 2 seconds. onCopy can be async: the confirmation appears when the promise resolves, and nothing appears when it rejects.

Edit in place

onEdit enables the in-place edition of a text message: the edit button turns the bubble into an editor (see ChatMessageEditor) with Cancel and Send buttons. onEdit is called with the new text when it is sent — it is up to you to replace the message, and to drop or regenerate the answers that followed it.

The edit action is not offered when children is not a string (for instance rendered Markdown).

Translate the labels

labels translates the accessible names, the tooltips and the editor buttons. Every entry is optional.

<ChatMessage
  sender="user"
  onReplay={() => {}}
  onEdit={() => {}}
  onCopy={() => {}}
  labels={{
    replay: 'Rejouer',
    edit: 'Modifier',
    copy: 'Copier',
    copied: 'Copié',
    cancel: 'Annuler',
    send: 'Envoyer',
  }}
>
  Bonjour
</ChatMessage>

Rich content

children can be any node, for instance rendered Markdown. The bubble is then a div, which can hold block elements (lists, code blocks), instead of a p.

API Reference

ChatMessage

PropTypeDefaultDescription
sender"assistant" | "user"—Required. Determines alignment and styling
childrenReactNode—Message content. Text is rendered in a p, any other node in a div
onReplay() => voidundefinedUser messages only. Renders the replay action
onEdit(content: string) => voidundefinedUser text messages only. Enables the in-place edition, called with the new text
onCopy() => void | Promise<void>undefinedUser messages only. Renders the copy action, confirmed with a check once it succeeded
labelsPartial<ChatMessageLabels>EnglishLabels of the actions and of the editor. See ChatMessageActions
classNamestringundefinedAdditional CSS classes

Inherits all HTMLDivElement attributes.

Notes

  • Assistant messages are left-aligned with no background.
  • User messages are right-aligned with a pearl background pill.
  • The actions and the editor are built with ChatMessageActions and ChatMessageEditor.
  • Both use role="article" with a descriptive aria-label for screen reader support.
  • Used inside ChatWindow as part of the message list.