suiss/uim · React Native Component

Message

A chat message row that lays out an avatar, a group of bubbles, and an optional header and footer.

Message is an open-source mobile component from suiss/uim for React Native and Expo, with SwiftUI and Jetpack Compose versions built from the same design tokens. The @esuiss/uim CLI copies its source into your project, so you own the code.

iOS
Message on iOS in light mode

Message is a horizontal View that places an avatar next to a column of bubbles. Its align prop puts the row on the leading (start) or trailing (end) side and passes the alignment to any Bubble inside through context.

MessageGroup stacks several messages into a conversation. MessageHeader and MessageFooter hold small secondary text, such as a sender name or a timestamp.

Installation

$ pnpm dlx @esuiss/uim@latest add message

The CLI also adds what it builds on: Bubble, Icon, Text.

Manual

  1. Install the dependencies:
npx expo install cn
  1. Add the pieces it builds on: Bubble, Icon, Text.

  2. Copy the source into components/ui/message.tsx:

uniwind/components/ui/message.tsx
import { cn } from "cn"
import * as React from "react"
import { View, type ViewProps } from "react-native"
import { BubbleAlignContext, BubbleGroup } from "@/components/ui/bubble"
import { IconClassContext } from "@/components/ui/icon"
import { Text, TextClassContext } from "@/components/ui/text"

type MessageAlign = "start" | "end"

const MessageContext = React.createContext<{
  align: MessageAlign
  footer: boolean
}>({ align: "start", footer: false })

function wrapText(children: React.ReactNode) {
  const output: React.ReactNode[] = []
  1. Update the import paths to match your project.

Usage

import { MessageGroup, Message, MessageAvatar, MessageContent, MessageHeader, MessageFooter } from "@/components/ui/message"
<Message align="start">
  <MessageAvatar>
    <Avatar>
      <AvatarFallback>MK</AvatarFallback>
    </Avatar>
  </MessageAvatar>
  <MessageContent>
    <Bubble variant="muted">
      <BubbleContent>Are we still on for lunch tomorrow?</BubbleContent>
    </Bubble>
  </MessageContent>
</Message>

Put a MessageAvatar and a MessageContent inside each Message, and place Bubble components, a MessageHeader and a MessageFooter in MessageContent.

Plain strings in MessageHeader and MessageFooter are wrapped in Text for you, so you can pass text directly or mix it with an Icon.

When MessageContent contains a MessageFooter, the avatar shifts up so it lines up with the last bubble instead of the footer.

Examples

Align end

A message aligned to the trailing side, as for the current user.

iOS
Message (align end) on iOS in light mode

A message with a sender name above and a timestamp below.

iOS
Message (with header and footer) on iOS in light mode

Conversation

A MessageGroup with an incoming and an outgoing message, including a read receipt footer.

iOS
Message (conversation) on iOS in light mode

API Reference

MessageGroup

Vertical stack of messages.

ExtendsViewProps

Message

Row that holds the avatar and content and sets the alignment.

ExtendsViewProps
PropType
About
Side of the row: start (default) or end. Also aligns nested bubbles and the footer.
Type
MessageAlign
Default
"start"

MessageAvatar

Round slot for an avatar, pinned to the bottom of the row.

ExtendsViewProps

MessageContent

Column of bubbles built on BubbleGroup.

ExtendsReact.ComponentProps<typeof BubbleGroup>

MessageHeader

Small secondary text above the bubbles.

ExtendsViewProps

MessageFooter

Small secondary text below the bubbles, aligned with the message side.

ExtendsViewProps

Accessibility

  • Messages are plain View and Text elements, so screen readers read the header, bubble text and footer in order.
  • Give avatars without visible text, such as images, an accessibilityLabel.

Notes

  • Header and footer text use text-xs text-label-secondary, with icons at size-3.5.
  • The avatar slot uses bg-surface-secondary.
  • Light and dark colors follow the system appearance.