suiss/uim · React Native Component

Bubble

A chat message bubble with a tail, alignment, variants and emoji reactions.

Bubble 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
Bubble on iOS in light mode

Bubble lays out one message aligned to the start or end of the conversation, and BubbleContent draws the rounded bubble with an SVG tail on the aligned side. Seven variants are available: default (accent), secondary, muted, tinted, outline, ghost and destructive; outline and ghost have no tail.

BubbleGroup stacks messages and shows the tail only on the last bubble of each run from the same side, so consecutive messages read as one block. BubbleReactions pins a small reaction pill to the top or bottom edge of a bubble.

Installation

$ pnpm dlx @esuiss/uim@latest add bubble

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

Manual

  1. Install the dependencies:
npx expo install cn phosphor-react-native react-native-svg
  1. Add the pieces it builds on: Icon, Text, and the primitives components/primitives/types.tsx, components/primitives/use-render.tsx.

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

uniwind/components/ui/bubble.tsx
import { cn } from "cn"
import type { IconProps as PhosphorIconProps } from "phosphor-react-native"
import * as React from "react"
import {
  type GestureResponderEvent,
  I18nManager,
  Pressable,
  View,
  type ViewProps,
} from "react-native"
import Svg, { Path } from "react-native-svg"
import type { RenderProp } from "@/components/primitives/types"
import { useRender } from "@/components/primitives/use-render"
import { Icon, IconClassContext } from "@/components/ui/icon"
import { Text, TextClassContext } from "@/components/ui/text"
  1. Update the import paths to match your project.

Usage

import { BubbleGroup, Bubble, BubbleContent, BubbleReactions } from "@/components/ui/bubble"
<Bubble align="start">
  <BubbleContent>Are we still on for lunch tomorrow?</BubbleContent>
</Bubble>

Wrap each message in Bubble and put its text in BubbleContent; plain strings are wrapped in Text. Set align="end" for messages from the current user, or provide a default through BubbleAlignContext.

BubbleContent becomes a Pressable when you pass onPress, onLongPress or render, and its background darkens while pressed. Use render to attach it to a trigger such as a context menu.

BubbleReactions takes side (top or bottom) and align (start or end), and the bubble adds margin so the pill does not overlap its neighbors.

Examples

Variants

All seven variants.

iOS
Bubble (variants) on iOS in light mode

Conversation

A BubbleGroup with messages from both sides and merged tails.

iOS
Bubble (conversation) on iOS in light mode

With reactions

Reaction pills on the bottom and top edges.

iOS
Bubble (with reactions) on iOS in light mode

Interactive

A pressable outline bubble with onPress.

iOS
Bubble (interactive) on iOS in light mode

Long press actions

A bubble that opens a context menu on long press.

iOS
Bubble (long press actions) on iOS in light mode

API Reference

BubbleGroup

Column that decides which bubbles show a tail.

ExtendsViewProps

Bubble

One message, aligned to the start or end.

ExtendsViewProps
PropType
About
default, secondary, muted, tinted, outline, ghost or destructive.
Type
BubbleVariant
Default
"default"
About
start or end. Defaults to start.
Type
BubbleAlign
Default
—

BubbleContent

The bubble itself, optionally pressable.

ExtendsReact.ComponentProps<typeof Pressable>React.RefAttributes<View>
PropType
About
Element to render instead of the default view or pressable.
Type
RenderProp<Record<string, unknown>, Record<string, never>>
Default
—

BubbleReactions

Reaction pill on the edge of a bubble.

ExtendsViewProps
PropType
About
start or end along the bubble. Defaults to end.
Type
BubbleAlign
Default
"end"
About
top or bottom. Defaults to bottom.
Type
"top""bottom"
Default
"bottom"

Accessibility

  • BubbleContent gets the button role when it has onPress or onLongPress.
  • The tail is hidden from VoiceOver and TalkBack.
  • Give long-press bubbles an accessibilityLabel that says who sent the message.

Notes

  • The tail mirrors itself for end alignment and in right-to-left layouts.
  • Uses bg-accent, bg-control, bg-surface-secondary and bg-danger-surface tokens with pressed states; light and dark follow the system appearance.