Skip to main content
Gremorie

Message

Atomic chat turn with role-aware layout, attachments, branch navigation and a streaming markdown surface.

Overview

Message renders a single chat turn. It picks layout, alignment and surface treatment from the from role ("user" vs "assistant"), then lets you compose the inside with content, attachments, a toolbar and branch navigation. The streaming markdown is rendered by MessageResponse, which is memoized to keep large turns smooth while tokens stream in.

Use it as the atomic unit of any conversation: one Message per turn, composed with siblings like Reasoning, Sources or InlineCitation.

Preview

Assistant with toolbar

How can I help?

I can answer questions, summarize documents, and walk through code with you. Pick a task:

  • Explain a concept
  • Refactor a function
  • Draft an email

Just let me know what you need.

User message

Can you summarize the latest Gremorie release notes in three bullet points?

Rich markdown response

Here's the breakdown:

  1. Speed — caching layer responds in under 50ms.
  2. Coverage — 100 primitives shipped across 9 packages.
  3. DXnpx gremorie add rx-message is the single entry point.

The registry is now the source of truth.

ts
import { Message } from "@gremorie/rx-ai";

User and assistant pair

What is Gremorie?

Gremorie is an AI-native design system distributed via registry and MCP. It ships React and Angular primitives that AI agents can pull on demand.

Installation

bash npx gremorie@latest add rx-message
bash pnpm dlx gremorie@latest add rx-message
bash yarn dlx gremorie@latest add rx-message
bash bunx --bun gremorie@latest add rx-message

Usage

import {
  Message,
  MessageContent,
  MessageResponse,
} from "@gremorie/rx-ai";

export function Example() {
  return (
    <Message from="assistant">
      <MessageContent>
        <MessageResponse>{markdown}</MessageResponse>
      </MessageContent>
    </Message>
  );
}
import { Component } from "@angular/core";
import {
  Message,
  MessageContent,
  MessageResponse,
} from "@gremorie/ng-ai";

@Component({
selector: "app-example",
standalone: true,
imports: [Message, MessageContent, MessageResponse],
template: `     <message from="assistant">
      <message-content>
        <message-response>{{ markdown }}</message-response>
      </message-content>
    </message>
  `,
})
export class ExampleComponent {
readonly markdown = "Hello! How can I help you today?";
}

API

<Message>

PropTypeDefaultDescription
from"user" | "assistant" | "system"-Role of the turn. Drives alignment and surface treatment via is-user / is-assistant group classes.
classNamestring-Extra classes on the root div.

Extends all HTMLAttributes<HTMLDivElement>.

<MessageContent>

Container for the turn body. Picks up styling from the parent Message via group selectors (e.g. user turns get a rounded secondary surface).

Extends all HTMLAttributes<HTMLDivElement>.

<MessageResponse>

PropTypeDefaultDescription
childrenstring-Markdown source. Re-renders are gated by string equality.

Wraps Streamdown for incremental token-by-token rendering.

<MessageAttachments> / <MessageAttachment>

PropTypeDefaultDescription
dataFileUIPart-Attachment payload (filename, mediaType, url).
onRemove() => void-Optional remove handler. Renders an X button on hover.

<MessageToolbar> / <MessageActions> / <MessageAction>

PropTypeDefaultDescription
tooltipstring-Tooltip label rendered above the action. Doubles as sr-only text.
labelstring-Explicit accessible label (overrides tooltip for sr-only text).
variantButtonVariant"ghost"Forwarded to the underlying Button.
sizeButtonSize"icon-sm"Forwarded to the underlying Button.

<MessageBranch> (compound)

PropTypeDefaultDescription
defaultBranchnumber0Initial branch index.
onBranchChange(index: number) => void-Fires when the user navigates between branches.

Provides context for MessageBranchContent, MessageBranchSelector, MessageBranchPrevious, MessageBranchNext and MessageBranchPage.

Composition

  1. <Message> sets alignment and exposes the role via group classes.
  2. <MessageContent> is the surface that adapts to user vs assistant.
  3. <MessageResponse> renders streaming markdown inside the content.
  4. <MessageAttachments> / <MessageAttachment> sit above the content for file previews.
  5. <MessageToolbar> + <MessageActions> + <MessageAction> appear under the content for copy, regenerate, like and other affordances.
  6. <MessageBranch> wraps several <MessageBranchContent> siblings so users can step through alternate completions.

Variations

Streaming assistant response

Use while tokens are still arriving. Pair with Reasoning to expose thinking.

<Message from="assistant">
  <MessageContent>
    <Reasoning isStreaming={status === "streaming"}>
      <ReasoningTrigger />
      <ReasoningContent>{thinking}</ReasoningContent>
    </Reasoning>
    <MessageResponse>{partialMarkdown}</MessageResponse>
  </MessageContent>
</Message>

User turn with attachments

User attachments float above the bubble. The attachment row aligns to the trailing edge to match the bubble's right alignment.

<Message from="user">
  <MessageAttachments>
    {files.map((file) => (
      <MessageAttachment
        key={file.url}
        data={file}
        onRemove={() => removeFile(file.url)}
      />
    ))}
  </MessageAttachments>
  <MessageContent>{prompt}</MessageContent>
</Message>

Assistant turn with branch navigation and toolbar

Lets users walk through regenerated alternates and act on the current one.

<Message from="assistant">
  <MessageBranch>
    <MessageBranchContent>
      <MessageContent>
        <MessageResponse>{variantA}</MessageResponse>
      </MessageContent>
    </MessageBranchContent>
    <MessageBranchContent>
      <MessageContent>
        <MessageResponse>{variantB}</MessageResponse>
      </MessageContent>
    </MessageBranchContent>
  </MessageBranch>
  <MessageToolbar>
    <MessageBranchSelector from="assistant">
      <MessageBranchPrevious />
      <MessageBranchPage />
      <MessageBranchNext />
    </MessageBranchSelector>
    <MessageActions>
      <MessageAction tooltip="Copy" onClick={copy}>
        <CopyIcon />
      </MessageAction>
      <MessageAction tooltip="Regenerate" onClick={regenerate}>
        <RefreshIcon />
      </MessageAction>
    </MessageActions>
  </MessageToolbar>
</Message>

Accessibility

  • Keyboard: MessageAction and branch controls are real Buttons, so they participate in normal Tab order and respond to Enter / Space.
  • ARIA: MessageAction reads tooltip (or label) as sr-only text, so icon-only actions still announce. MessageBranchPrevious and MessageBranchNext carry explicit aria-labels ("Previous branch", "Next branch"). MessageAttachment remove buttons announce as "Remove attachment".
  • Screen readers: Branch controls disable themselves (and announce as disabled) whenever there is only one branch. The selector hides itself entirely below two branches.
  • Focus management: Toolbar actions stay in document order so screen readers narrate the turn before its affordances.
  • Conversation - scroll-to-bottom container for a stream of messages
  • Reasoning - collapsible thinking block to embed inside a Message
  • Sources - cite the documents an assistant turn used
  • InlineCitation - inline footnote-style citations inside MessageResponse

On this page