suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
  • AI
GitHubCreate
DocsComponentsPatternsInteractionsChartsBlocksAICreate
AI21
  • Overview
Conversation
  • Chat
  • Message
  • Prompt Input
  • Suggestion
  • Model Selector
  • Context
  • Checkpoint
  • Inline Citation
Agent steps
  • Chain of Thought
  • Reasoning
  • Tool
  • Confirmation
  • Task
  • Plan
  • Queue
  • Subagent
  • Sources
Output
  • Terminal
  • Stack Trace
  • Test Results
  • File Tree

suiss/ui · AI

Message

One turn of a conversation: the person's message, and the agent's reply with actions to copy, rate and retry it.

"use client"

import {
  ArrowsClockwiseIcon,
  CheckIcon,
  CopyIcon,
  PencilSimpleIcon,
  ThumbsDownIcon,
  ThumbsUpIcon,
} from "@phosphor-icons/react"
import * as React from "react"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Button } from "@/components/ui/button"
import { CodeBlock, CodeBlockContent } from "@/components/ui/code-block"
import { Message, MessageContent, MessageFooter } from "@/components/ui/message"
import {

Use this pattern for one turn in a thread. The person's text is a Bubble on a Message with align="end". The agent's reply is plain content on a Message, with actions under it. A thread of these turns is a Chat.

Installation

The pattern is built with Message, Bubble, Button and Tooltip.

$ pnpm dlx shadcn@latest add @esuiss/message @esuiss/bubble @esuiss/button @esuiss/tooltip

Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add message bubble button tooltip

Usage

import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Button } from "@/components/ui/button"
import { Message, MessageContent, MessageFooter } from "@/components/ui/message"
<Message align="end">
  <MessageContent>
    <Bubble align="end" variant="muted">
      <BubbleContent>How do I add a settings dialog?</BubbleContent>
    </Bubble>
  </MessageContent>
</Message>
<Message>
  <MessageContent className="gap-2">
    <p>Install Dialog, then open it from the gear button.</p>
    <MessageFooter>
      <Button variant="ghost" size="icon-sm" aria-label="Copy">
        <CopyIcon />
      </Button>
    </MessageFooter>
  </MessageContent>
</Message>

Put actions in MessageFooter. It follows the message alignment, so on the person's turn the actions sit at the end. Use ghost icon buttons with a tooltip for each:

  • Copy writes the reply to the clipboard and swaps to a check for two seconds.
  • Good response and Bad response are toggles. Set aria-pressed so screen readers announce the state.
  • Retry runs the turn again.
  • Edit on the person's turn puts the text back in the field.

Render markdown replies with your markdown renderer of choice, and route fenced code to a Code Block.

AI SDK

Branch on the message role. The person's turn is the bubble. The agent's turn is the reply, and regenerate with the message id runs it again.

const { messages, regenerate } = useChat()

if (message.role === "user") {
  return (
    <Message align="end">
      <MessageContent>
        <Bubble align="end" variant="muted">
          <BubbleContent>{text}</BubbleContent>
        </Bubble>
      </MessageContent>
    </Message>
  )
}

return (
  <Message>
    <MessageContent className="gap-2">
      <Markdown>{text}</Markdown>
      <MessageFooter>
        <Button
          variant="ghost"
          size="icon-sm"
          aria-label="Retry"
          onClick={() => regenerate({ messageId: message.id })}
        >
          <RefreshCcwIcon />
        </Button>
      </MessageFooter>
    </MessageContent>
  </Message>
)

API Reference

See Message, Bubble and Tooltip.

Accessibility

  • Every action is an icon button, so give each an aria-label; the tooltip repeats it for pointer users.
  • Change the label when the state changes, such as "Copy" to "Copied", so the change is announced.
  • Feedback buttons are toggles and set aria-pressed.
import { ArrowClockwiseIcon, ThumbsDownIcon, ThumbsUpIcon } from "phosphor-react-native"
import { View } from "react-native"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Button } from "@/components/ui/button"
import { CodeBlock, CodeBlockActions, CodeBlockContent, CodeBlockCopyButton, CodeBlockHeader, CodeBlockTitle } from "@/components/ui/code-block"
import { Icon } from "@/components/ui/icon"
import { Message, MessageContent } from "@/components/ui/message"
import { Text } from "@/components/ui/text"

const command = "npx @esuiss/ui@latest add dialog"

export function AiMessageExample() {
  return (
    <View className="w-full gap-4">
      <Message align="end">
        <MessageContent>
import EsuissUI
import PhosphorSwift
import SwiftUI

struct MessageExample: View {
  var body: some View {
    VStack(alignment: .leading, spacing: 16) {
      Message(align: .end) {
        MessageContent {
          Bubble(variant: .muted, align: .end) { BubbleContent("How do I add a dialog?") }
        }
      }
      VStack(alignment: .leading, spacing: 12) {
        Text("Add it with the CLI, then import it where you need it.").textStyle(.base).foregroundStyle(EsuissColor.foreground)
        CodeBlock(size: .sm) {
          CodeBlockHeader {
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.ArrowClockwise
import com.adamglin.phosphoricons.regular.ThumbsDown
import com.adamglin.phosphoricons.regular.ThumbsUp
import com.esuiss.ui.components.Bubble
import com.esuiss.ui.components.BubbleContent
import com.esuiss.ui.components.BubbleVariant
import com.esuiss.ui.components.Button

Message on mobile is built from the same parts as on the web: Message, Bubble, Code Block, Button. The example above is a static demo; connect it to your agent by rendering the same parts from your messages.

Installation

$ pnpm dlx @esuiss/ui@latest add message bubble code-block button
$ pnpm dlx @esuiss/ui@latest add message bubble code-block button --platform swift
$ pnpm dlx @esuiss/ui@latest add message bubble code-block button --platform kotlin

API Reference

See Message, Bubble, Code Block, Button.

NextPrompt InputPreviousChat

On this page

  1. 01Installation
  2. 02Usage
  3. 03AI SDK
  4. 04API Reference
  5. 05Accessibility
  6. 06Installation
  7. 07API Reference