Documentation
Pixel-accurate iMessage UI for React
imessage-ui is the component kit behind this site's group-chat simulator, homepage message board and intake bot. Its flagship, IMessageGlass, renders the whole iOS 26 Messages surface — calibrated pixel-for-pixel against a real iPhone, with engine-glass chrome that refracts the thread and full group-chat support. Around it: 15 components — tailed bubbles with the SwiftUI-accurate curl, grouping, tapbacks, typing indicators, headers — plus the hooks that make a chat feel native (keyboard tracking, long-press gestures). Framework-agnostic React: no Next.js dependency, ESM, typed.
Installation
imessage-ui is a private, proprietary package (not on npm). It installs from a packed tarball — built with npm pack from the workspace — as pre-compiled ESM that works in Next, Vite, anything React 18+; no transpilePackages needed.
npm install ./imessage-ui-0.3.0.tgz react react-domImport the stylesheet once (it carries the global bubble primitives and the component styles), then compose:
// app/layout.tsx (or your entry point)
import "imessage-ui/styles.css";import { IMessageScreen, ChatBubble } from "imessage-ui";
export function Hello() {
return (
<IMessageScreen>
<ChatBubble variant="received" text="hey 👋" senderName="Riley" />
<ChatBubble variant="sent" text="oh hey" receipt="read" />
</IMessageScreen>
);
}Monorepo / source mode
Consuming the package from source (as this site does)? Add it to transpilePackages and import "imessage-ui/css"— the global layer only; component styles load through CSS modules. The published tarball's combined stylesheet is "imessage-ui/styles.css".
Theming
Every color flows through --imsg-* custom properties established by IMessageScreen (or IMessageThread). Tokens default to the light palette and flip automatically under a .dark ancestor — so the kit follows your existing dark-mode setup. Override any token with plain CSS to re-skin.
| Token | Controls |
|---|---|
| --imsg-text | Message text color |
| --imsg-bubble | Received-bubble fill |
| --imsg-blue | Sent-bubble fill (iMessage blue) |
| --imsg-name | Sender-name label color |
| --imsg-screen-bg | Chat backdrop |
| --imsg-divider | Hairline dividers |
| --imsg-input-bg / --imsg-input-border | Composer pill |
For the iOS 26 look, pass variant="glass" (plus a wallpaper) to Screen or Thread — bubbles and chrome turn translucent and frost whatever sits behind them.
Components
- IMessageGlassThe iOS 26 Messages surface, calibrated 1:1 against an iPhone 15 Plus: a live interactive thread whose floating chrome (nav capsules, input pill) is real engine glass that refracts the messages scrolling behind it. One component renders the whole phone — status bar, header, thread, composer — in any size container. Supports 1:1 conversations (contact) and multi-sender group chats (group) with sender labels, gutter avatars and the member-cluster header.
- ChatBubbleThe high-level message bubble: grouping-aware tails, avatar gutter, sender names, read receipts, big-emoji rendering and tapback reactions. This is the component the group-chat simulator, message board and intake bot are built from.
- HeroBubbleThe low-level tailed bubble with the SwiftUI-accurate tail curl. Two modes: fixed-width (server-rendered at an exact baked width — zero layout shift; the homepage hero uses this) and multiline (wraps and self-measures with ResizeObserver).
- ImageBubbleAn image attachment in the same row/avatar/sender-name layout as ChatBubble. Aspect ratio comes from the attachment's intrinsic dimensions, so nothing shifts while the image loads.
- TypingIndicatorThe tailed gray bubble with three pulsing dots — the real iMessage typing indicator is a received bubble with the same bottom-left tail curl, not a plain pill.
- AvatarCircleThe gradient-initial chip (or photo) used everywhere an avatar appears — bubbles, headers, clusters. Framework-agnostic: plain <img>, no Next dependency.
- ChatHeaderThe classic iOS group-chat header: staggered avatar cluster (Figma-transcribed slot positions), back button and title/subtitle. Fixed full-screen chrome by default, or static with `embedded`.
- LiquidGlassHeaderThe iOS 26 header: floating frosted-glass capsules (back pill with unread count, centred avatar/name pill, FaceTime button) hovering over the messages. Pure CSS — cross-browser, cheap, auto-themed from the tokens.
- InputBarThe auto-growing iMessage composer: pill textarea + spring-loaded send button. Enter sends (IME-safe, Shift+Enter for newlines), and it deliberately never steals focus — programmatic focus is what causes iOS Safari's keyboard bounce.
- ReactionBubble & ReactionGlyphTapbacks. ReactionBubble is the laid reaction — the 29px circle with two trailing dots, mirrored for sent messages, with a count badge for stacks. ReactionGlyph is the bare icon for pickers and custom UI.
- TapbackPickerThe floating reaction row that springs up on a long-press. Controlled: you own `open` and wire the gesture (useLongPress pairs perfectly). Dismisses on scrim tap or Escape.
- IMessageScreenThe token scope. Establishes the full --imsg-* palette (light by default, flipped by a .dark ancestor), base typography and the chat backdrop — without imposing any layout, so it works as a full screen or a small embedded panel.
- IMessageThreadThe ready-made container: token scope + a scrollbar-less scroll area (iMessage has none) + header/footer slots, with the footer riding above the mobile keyboard via useKeyboardInset. The three things every chat surface re-implements, bundled.
- LiquidGlassA real refraction lens over live DOM (re-exported from liquid-glass-web-react). Unlike a backdrop-filter hack, the lens refracts its children — which stay selectable, clickable and scrollable. WebGL generates the displacement map; the render is a plain SVG filter.
- LiquidGlassRefractPhysically-based glass without WebGL: a circular-arc bevel profile drives an feDisplacementMap, so the rim genuinely inverts edges the way thick glass does (not a magnifying-ramp approximation). Cross-browser SVG filters.