Skip to content

Chat Message

Display a conversation turn with actions such as copy, regenerate, rate and edit.

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use one for every turn in a conversation, inside a Chat Message List, which groups consecutive messages and announces them. An assistant message renders its content as streamed markdown across the column. A user message is a bubble on the inline-end side, and a system message is a centered notice between hairlines. Hover the answer in the preview to see its actions, or edit the question.

import { ChatMessage } from '@youj/react'

<ChatMessage role="user" name="Ada Lovelace" content="Why did checkout get slower?" onEdit={resend} />
<ChatMessage
  role="assistant"
  name="Youj"
  content={reply.stream}
  streaming={reply.streaming}
  feedback={reply.feedback}
  onFeedbackChange={rate}
  onRegenerate={regenerate}
/>

Names default to "Assistant", "You" and "System". timestamp takes a Date, a string or a number, and shows the time with the full date in its title. Messages are memoised, so pass stable callbacks and a message re-renders only when its own props change.

children render before the text, for what the assistant did first: a typing indicator or reasoning, tool calls, attachments. footer renders after it, for follow-ups. sources turns [1] in the text into a citation and adds a row of sources under the answer once it's done.

Reference

Propsrole, name, avatar… (27)

Props

ChatMessageProps props
PropType
roleRequiredChatRole
'user''assistant''system'
–
namestring

The author, shown and announced. Defaults: "Assistant", "You", "System".

–
avatarReactNode

A custom avatar, e.g. <Avatar><AvatarImage src={…} /><AvatarFallback>AL</AvatarFallback></Avatar>.

–
timestampDate | string | number–
contentChatText

The text. Markdown for assistant messages, plain text otherwise. A ChatTextSource streams it.

–
streamingboolean

The assistant is still writing it: the text streams in, and actions wait until it's done.

–
childrenReactNode

Blocks before the text: reasoning, tool calls, attachments.

–
footerReactNode

Blocks after the text: follow-ups, anything else.

–
sourcesChatSource[]

Where the answer came from: [1] in the text links to sources[0], and a row of chips follows the answer.

–
headingLevelnumber

The outline level for the answer's top heading. Default 2 (see ChatMarkdown).

–
errorstring

Why the message failed. Shown under whatever arrived, with Retry when onRetry is set.

–
incompleteboolean

The answer was cut off (stopped): its unfinished end renders as it did while streaming. Implied by error.

–
onRetry() => void–
onRegenerate() => void

Shows Regenerate on assistant messages.

–
feedbackChatFeedback
'up''down'

Good / bad response, controlled. Shows the pair when set or when onFeedbackChange is.

–
onFeedbackChange(feedback: ChatFeedback) => void–
onEdit(text: string) => void

Shows Edit on user messages and receives the new text.

–
actionsReactNode

More actions after the built-in ones: ChatMessageAction elements.

–
copyableboolean

Offer Copy.

default true

true
refRef<HTMLElement>

Allows getting a ref to the component instance. Once the component unmounts, React will set ref.current to null (or call the ref with null if you passed a callback ref).

–
Other props pass through: extends ComponentProps<'article'>.

ChatMessageAction

ChatMessageAction props
PropType
labelRequiredstring

The action's name and tooltip.

–
childrenRequiredReactNode–
onClick() => void–
pressedboolean

Makes it a toggle (aria-pressed), e.g. good response.

–
onPressedChange(pressed: boolean) => void–
disabledboolean–
refRef<HTMLButtonElement>–
MotionActions show and hide, writing, copied… · reduced motion

Motion

Motion tokens
MomentTokens
Actions show and hidethe toolbar fades on hover and focus
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)
Writingthe assistant avatar’s sparkles twinkle while it streams
no token

loop

loop
Copiedthe back sheet tucks in behind the front one
duration.baseease.exit

220ms · cubic-bezier(0.4, 0, 1, 1)

220mscubic-bezier(0.4, 0, 1, 1)
Copied checkwritten on the page, after a base delay
duration.slow

340ms

340ms
Copied holdbefore the icon and the tooltip return to rest
duration.deliberate

560ms · × 3

560ms× 3
Good or bad responsethe thumb rocks −16°, 5°, 0° as it turns on
duration.deliberateease.emphasized

560ms · cubic-bezier(0.6, 0, 0.1, 1)

560mscubic-bezier(0.6, 0, 0.1, 1)
Error markthe warning rocks once as it mounts
duration.deliberateease.standard

560ms · cubic-bezier(0.2, 0, 0, 1)

560mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

The toolbar shows and hides at once, the avatar holds still, and the icons change without playing.

The text's own motion (words fading in, the caret) belongs to Chat Markdown.

Reduced motion

The toolbar appears without fading, the assistant's avatar stops twinkling, and the copy, rating and warning icons change state without animating. Streamed text arrives in whole sentences (see Chat Markdown).

AccessibilityKeyboard, ARIA, focus

Accessibility

  • Each message is an article named by its header, the author and the time, even when grouping hides the header visually.
  • While streaming it sets aria-busy. Inside a list, a finished assistant answer is announced once, whole, and a failure assertively.
  • The actions are one toolbar named "Message actions": a single tab stop, with arrow keys moving inside it. Every action has a name and a tooltip, and the rating buttons are toggles with aria-pressed.
  • The edit field is named "Edit message" and takes focus with the caret at the end. Escape cancels and returns focus to Edit.