# Chat screen

> A complete one-to-one chat thread: a header with the contact's avatar, presence and call buttons, messages grouped by day and by sender, receipts that tick forward, typing dots, long-press reactions and a composer that rides the keyboard; a sent message springs up out of the composer and ticks from clock to read.

- Type: Component (screens)
- Access: Pro
- Page: https://goodui.dev/native/components/chat-screen
- Markdown: https://goodui.dev/native/components/chat-screen.md
- Registry item: https://goodui.dev/native/r/chat-screen.json
- Dependencies: react-native-reanimated, react-native-svg
- Keywords: react native chat screen, messaging thread, expo chat ui template, chat bubbles with receipts, typing indicator, message reactions, keyboard aware composer

## When to use

- The thread of a messaging, dating, marketplace or support app: one person at the other end.
- Any conversation where delivery state matters to the sender (orders, bookings, payments between people).

## When not to use

- Group chats with several senders: compose ChatBubble with its sender prop yourself; this screen labels runs by side only.
- Thousands of messages in memory: the thread is a ScrollView; page older history or move the bubbles into an inverted FlatList.
- An AI assistant with streaming answers and markdown: bubbles hold plain text.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add @goodui-native-pro/chat-screen
npx expo install --fix
```

Pro: add `"@goodui-native-pro": { "url": "https://goodui.dev/native/r/pro/{name}.json", "headers": { "Authorization": "Bearer ${GOODUI_LICENSE_KEY}" } }` to the `registries` of components.json and put your license key in .env.local.

The component lands in `components/goodui-native/chat-screen/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs avatar, chat-bubble, icon-button, locale.

### Setup

- Wrap the app in GestureHandlerRootView and ThemeProvider.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
// app/chat/[id].tsx
import { useState } from "react";
import { Stack, router, useLocalSearchParams } from "expo-router";
import { ChatScreen, type ChatMessage } from "@/components/goodui-native/chat-screen/chat-screen";
import { api, useThread } from "@/lib/chat";

export default function ChatRoute() {
  const { id } = useLocalSearchParams<{ id: string }>();
  const { contact, messages, typing } = useThread(id); // your realtime source, oldest first
  return (
    <>
      <Stack.Screen options={{ headerShown: false }} />
      <ChatScreen
        contact={contact}
        messages={messages}
        typing={typing}
        onSend={(text) => api.send(id, text)} // append it to messages with status "sending", then move it forward
        onRetry={(m) => api.resend(id, m.id)}
        onReactionChange={(messageId, reaction) => api.react(id, messageId, reaction)}
        onBack={() => router.back()}
        onCall={() => router.push(`/call/${id}`)}
        onContactPress={() => router.push(`/profile/${contact.id}`)}
      />
    </>
  );
}
```

## Examples

### Uncontrolled: the screen keeps the thread and drives the receipts

```tsx
<ChatScreen
  contact={{ name: "Leo Martins", online: true }}
  defaultMessages={history}
  onSend={(text) => api.send(text)} // pending: clock; resolved: sent, then delivered; rejected: retry row
  onBack={() => router.back()}
/>
```

### On its own, with a sample thread

```tsx
<ChatScreen />
```

## API reference

### ChatScreen

`import { ChatScreen } from "@/components/goodui-native/chat-screen/chat-screen";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `contact` | `ChatContact` | `sampleContact` | The person at the other end. |
| `messages` | `ChatMessage[]` | – | The thread (controlled), oldest first. With it, `onSend` only reports the text and you append the message and move its status forward yourself. |
| `defaultMessages` | `ChatMessage[]` | `sampleMessages` | The starting thread when uncontrolled. The screen then appends what you send and drives the receipts from `onSend`: sending while it is pending, sent then delivered when it resolves, failed with a retry when it rejects. |
| `onSend` | `(text: string) => unknown` | – | Called with the trimmed text when the person sends. Uncontrolled, return a promise to drive the receipts. |
| `onRetry` | `(message: ChatMessage) => unknown` | – | Retry was tapped under a failed message. Uncontrolled, the screen resends through `onSend` when omitted. |
| `onReactionChange` | `(id: string, reaction: string \| null) => void` | – | Your reaction on a message changed (a key, or null when removed). |
| `typing` | `boolean` | `false` | The contact is typing: dots in the thread and "Typing…" under the name. |
| `onBack` | `() => void` | – | Shows a back button. |
| `onCall` | `() => void` | – | Shows a call button. |
| `onVideoCall` | `() => void` | – | Shows a video call button. |
| `onContactPress` | `() => void` | – | Tapping the name or avatar, for example to open the profile. |
| `onAttach` | `() => void` | – | Shows an attach button before the field. |
| `onMicPress` | `() => void` | – | With it, the empty send button is a microphone. |
| `placeholder` | `string` | – | Composer placeholder. Default "Message". Same as `labels.placeholder`. |
| `locale` | `string` | – | BCP 47 locale for times and day labels. Defaults to the device locale. |
| `labels` | `Partial<ChatScreenLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- Day labels are headers; each bubble is one element read as 'You: text, 9:41, read' or 'Message: text, 9:41' for received ones, adding 'you reacted with Love' when you reacted.
- Reactions are also in each bubble's screen reader actions ('React with Love'), so nobody has to long-press. A failed message has a real 'Not delivered, tap to retry' button.
- The header names the contact with their presence ('Maya Patel, Online' or 'Typing…'); with onContactPress it is a button.
- Back, call, video and attach are icon buttons with labels; the typing dots announce '<name> is typing'.
- On the web the thread is a log region named 'Conversation with <name>'.

## Motion

- A sent message springs up out of the composer; incoming messages arrive from the leading edge. History on screen at mount never animates.
- Receipts tick forward in the bubble: a clock while sending, one check drawing in once sent, a second for delivered, both brightening when read.
- The thread follows new messages with an animated scroll only when you are already near the bottom, so reading history is never interrupted.
- The composer rides the keyboard with the keyboard's own duration (native); the web page resizes itself.
- Reduced motion: bubbles fade in place, the thread jumps to the newest message instead of scrolling.

## Haptics

- tap on send, on the mic and on retry (Composer, ChatBubble)
- tap when the reaction pill opens, select for each emoji the finger slides onto and on the pick (ReactionPicker)
- tap on the back, call, video and attach buttons

## Performance

- Bubbles, receipts and the keyboard offset animate on the UI thread; React renders when messages, the draft or typing change.
- The thread is a ScrollView: comfortable up to a few hundred messages; page history beyond that.

## Notes for AI

- Controlled: pass messages (oldest first, time as Date) and append the sent message yourself in onSend with status 'sending', then update status as your server reports. Uncontrolled: pass defaultMessages and return a promise from onSend.
- Message ids must be stable: ids that were not on screen at mount are treated as new and spring in.
- Buttons appear only for the callbacks you pass: onBack, onCall, onVideoCall, onAttach, onMicPress, onContactPress.
- Drive typing from your realtime events; it shows the dots and 'Typing…' under the name.
- Render it full-bleed with the navigator header hidden (headerShown: false); it handles the top and bottom safe areas and the keyboard itself, so do not wrap it in KeyboardAvoidingView.

## Related

- [Chat bubble](https://goodui.dev/native/components/chat-bubble.md): Message bubbles that group into runs with one tail, carry a receipt that ticks forward (clock, a check that draws itself, two checks, both brightening when read) and spring up out of the composer; plus a TypingIndicator whose dots wave and a Composer whose mic morphs into a filled send arrow as soon as there is text.
- [Reaction picker](https://goodui.dev/native/components/reaction-picker.md): Long-press a message and a pill of emoji springs out of it one by one; slide the finger across and the emoji under it swells dock-style while its neighbours lean in; let go to pick and the emoji flies in an arc onto the message, landing in a badge with a pop.
- [Inbox screen](https://goodui.dev/native/components/inbox-screen.md): A complete messaging inbox: a large title that collapses into the bar, a search field, conversation rows with avatars, previews, times and unread badges, swipe to mute, archive or delete with Undo, and pull to refresh where new conversations drop in at the top and push the list down.
- [Avatar](https://goodui.dev/native/components/avatar.md): A photo that fades in over initials on a color picked from the name, with a presence dot and a story ring whose accent arc sweeps in when there is something new and away once seen; AvatarGroup stacks people with a +N counter and fans out on tap.
- [Icon button](https://goodui.dev/native/components/icon-button.md): A layered icon-only button whose surface and icon sink on touch down and release on different springs; it runs async actions itself, carries a badge and shows a hint on long press.

## Guidance for AI tools

Chat screen: A complete one-to-one chat thread: a header with the contact's avatar, presence and call buttons, messages grouped by day and by sender, receipts that tick forward, typing dots, long-press reactions and a composer that rides the keyboard; a sent message springs up out of the composer and ticks from clock to read. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
