@kesha-antonov/react-native-chat 4.4.0 โ†’ 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +365 -211
  2. package/lib/Bubble/index.d.ts +13 -0
  3. package/lib/Bubble/index.d.ts.map +1 -1
  4. package/lib/Bubble/index.js +84 -67
  5. package/lib/Bubble/index.js.map +1 -1
  6. package/lib/Chat/index.d.ts.map +1 -1
  7. package/lib/Chat/index.js +36 -15
  8. package/lib/Chat/index.js.map +1 -1
  9. package/lib/Chat/types.d.ts +12 -11
  10. package/lib/Chat/types.d.ts.map +1 -1
  11. package/lib/InputToolbar.d.ts.map +1 -1
  12. package/lib/InputToolbar.js +3 -0
  13. package/lib/InputToolbar.js.map +1 -1
  14. package/lib/MessageImage.d.ts.map +1 -1
  15. package/lib/MessageImage.js +13 -8
  16. package/lib/MessageImage.js.map +1 -1
  17. package/lib/MessagesContainer/components/DayAnimated/debug.d.ts +1 -1
  18. package/lib/MessagesContainer/components/DayAnimated/debug.d.ts.map +1 -1
  19. package/lib/MessagesContainer/components/DayAnimated/debug.js +2 -2
  20. package/lib/MessagesContainer/components/DayAnimated/debug.js.map +1 -1
  21. package/lib/MessagesContainer/components/DayAnimated/index.d.ts.map +1 -1
  22. package/lib/MessagesContainer/components/DayAnimated/index.js +5 -5
  23. package/lib/MessagesContainer/components/DayAnimated/index.js.map +1 -1
  24. package/lib/MessagesContainer/components/DayAnimated/useScrollGatedOpacity.d.ts.map +1 -1
  25. package/lib/MessagesContainer/components/DayAnimated/useScrollGatedOpacity.js +1 -1
  26. package/lib/MessagesContainer/components/DayAnimated/useScrollGatedOpacity.js.map +1 -1
  27. package/lib/MessagesContainer/components/Item/index.d.ts.map +1 -1
  28. package/lib/MessagesContainer/components/Item/index.js +42 -19
  29. package/lib/MessagesContainer/components/Item/index.js.map +1 -1
  30. package/lib/MessagesContainer/index.d.ts.map +1 -1
  31. package/lib/MessagesContainer/index.js +71 -42
  32. package/lib/MessagesContainer/index.js.map +1 -1
  33. package/lib/MessagesContainer/types.d.ts +1 -0
  34. package/lib/MessagesContainer/types.d.ts.map +1 -1
  35. package/lib/TypingIndicator/index.d.ts.map +1 -1
  36. package/lib/TypingIndicator/index.js +11 -5
  37. package/lib/TypingIndicator/index.js.map +1 -1
  38. package/lib/components/TouchableOpacity.d.ts.map +1 -1
  39. package/lib/components/TouchableOpacity.js +15 -4
  40. package/lib/components/TouchableOpacity.js.map +1 -1
  41. package/lib/hooks/useUpdateLayoutEffect.d.ts.map +1 -1
  42. package/lib/hooks/useUpdateLayoutEffect.js +3 -0
  43. package/lib/hooks/useUpdateLayoutEffect.js.map +1 -1
  44. package/lib/linkParser.d.ts +16 -1
  45. package/lib/linkParser.d.ts.map +1 -1
  46. package/lib/linkParser.js +109 -35
  47. package/lib/linkParser.js.map +1 -1
  48. package/llms.txt +175 -0
  49. package/package.json +21 -22
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  <h1 align="center">React Native Chat</h1>
13
13
 
14
14
  <p align="center">
15
- The most complete chat UI for React Native & Web
15
+ The most complete chat UI for React Native &amp; Web
16
16
  </p>
17
17
 
18
18
  <p align="center">
@@ -32,35 +32,110 @@
32
32
  <a href="docs/MIGRATION.md">Migrate in one command with the codemod โ†’</a>
33
33
  </p>
34
34
 
35
+ <p align="center">
36
+ <strong><a href="https://kesha-antonov.github.io/react-native-chat/">๐Ÿ“– Documentation</a></strong> &nbsp;ยท&nbsp;
37
+ <a href="https://kesha-antonov.github.io/react-native-chat/installation">Installation</a> &nbsp;ยท&nbsp;
38
+ <a href="https://kesha-antonov.github.io/react-native-chat/usage">Usage</a> &nbsp;ยท&nbsp;
39
+ <a href="https://kesha-antonov.github.io/react-native-chat/props">Props</a> &nbsp;ยท&nbsp;
40
+ <a href="https://kesha-antonov.github.io/react-native-chat/guides">Guides</a>
41
+ </p>
42
+
43
+ <p align="center">
44
+ <sub>Using it in production? A โญ helps other developers find the library.</sub>
45
+ </p>
46
+
47
+ <hr />
48
+
49
+ <table align="center">
50
+ <tr>
51
+ <td align="center" valign="top" width="190">
52
+ <a href="https://cryptoc-app.web.app/"><img src="https://cryptoc-app.web.app/img/icon.png?v=3" width="76" height="76" alt="cryptoc app icon" /></a>
53
+ <br /><br />
54
+ <img src="https://cryptoc-app.web.app/img/qr-get.png?v=2" width="124" height="124" alt="QR code that installs cryptoc" />
55
+ <br />
56
+ <sub>Scan to install</sub>
57
+ <br /><br />
58
+ <a href="https://apps.apple.com/app/cryptoc/id1333169178"><img height="40" src="https://cryptoc-app.web.app/img/appstore.svg?v=2" alt="Download on the App Store" /></a>
59
+ <br />
60
+ <a href="https://play.google.com/store/apps/details?id=co.ssoul.CryptoC"><img height="59" src="https://cryptoc-app.web.app/img/googleplay.png?v=2" alt="Get it on Google Play" /></a>
61
+ </td>
62
+ <td valign="top">
63
+
64
+ ### Support my work
65
+
66
+ **[cryptoc](https://cryptoc-app.web.app/)** - my crypto portfolio app. Your coins on the home screen, lock screen and watch face. iPhone, iPad, Mac, Apple Watch, Android, Android tablet and Wear OS.
67
+
68
+ - Portfolio with average buy price and 24h / 180-day / all-time P&L
69
+ - Widgets in three sizes, refreshed in the background - most days you never open the app
70
+ - Price alerts on 5,000+ coins, delivered while the app is closed
71
+ - **No account, no email, no exchange API keys, no ads.** Your holdings never reach a server - they sync through your own iCloud or Google Drive
72
+ - Free for 3 holdings, and that is not a trial timer
73
+
74
+ <sub>Downloading it is what pays for the time that goes into these libraries.</sub>
75
+
76
+ </td>
77
+ </tr>
78
+ </table>
79
+
80
+ <hr />
81
+
82
+ ## Quick start
83
+
84
+ ```bash
85
+ npx expo install @kesha-antonov/react-native-chat react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
86
+ ```
87
+
88
+ ```jsx
89
+ import { useCallback, useState } from 'react'
90
+ import { Chat } from '@kesha-antonov/react-native-chat'
91
+
92
+ export function Example () {
93
+ const [messages, setMessages] = useState([
94
+ {
95
+ _id: 1,
96
+ text: 'Hello developer',
97
+ createdAt: new Date(),
98
+ user: { _id: 2, name: 'John Doe', avatar: 'https://placeimg.com/140/140/any' },
99
+ },
100
+ ])
101
+
102
+ const onSend = useCallback((newMessages = []) => {
103
+ setMessages(previous => Chat.append(previous, newMessages))
104
+ }, [])
105
+
106
+ return <Chat messages={messages} onSend={onSend} user={{ _id: 1 }} />
107
+ }
108
+ ```
109
+
110
+ That is the whole setup - everything below is optional.
111
+
35
112
  ---
36
113
 
37
114
  ## โœจ Features
38
115
 
39
- > Actively maintained, New Architecture ready, and built for the latest Reanimated/Gesture Handler. A modern, themeable, drop-in successor to `react-native-gifted-chat`. **[See what is new โ–ธ](#-whats-new-vs-react-native-gifted-chat)**
40
-
41
- - ๐ŸŒ— **[Modern UI, Dark Mode & Theming](#-whats-new-vs-react-native-gifted-chat)** - A clean, modern default look with a full light/dark theme system, runtime theme switching, and every token overridable
42
- - ๐Ÿค– **[Streaming (AI) Messages](#streaming-ai-messages)** - Token-by-token streamed replies with a typing cursor and stop control
43
- - ๐ŸŽฅ **[Video & Audio Messages](#-whats-new-vs-react-native-gifted-chat)** - Real inline playback (optional `expo-video` / `expo-audio`) with a graceful tappable fallback - no more "implement it yourself"
44
- - ๐ŸŽ™๏ธ **[Voice & Video Recording](#-whats-new-vs-react-native-gifted-chat)** - Telegram-style hold-to-record voice notes and camera video messages (optional, opt-in)
45
- - ๐Ÿ“ **[Location Messages](#-whats-new-vs-react-native-gifted-chat)** - Map card that opens the system maps app on tap
46
- - ๐ŸŽจ **[Fully Customizable](#-props-reference)** - Override any component with your own implementation
47
- - ๐Ÿ“Ž **[Composer Actions](#actions--action-sheet)** - Attach photos, files, or trigger custom actions
48
- - โ†ฉ๏ธ **[Reply to Messages](#reply-to-messages)** - Swipe-to-reply with reply preview and message threading
49
- - โฎ๏ธ **[Load Earlier Messages](#load-earlier-messages)** - Infinite scroll with pagination support
50
- - ๐Ÿ“‹ **[Copy to Clipboard](#copy-to-clipboard)** - Long-press messages to copy text
51
- - ๐Ÿ”— **[Smart Link Parsing](#smart-link-parsing)** - Auto-detect URLs, emails, phone numbers, hashtags, mentions
52
- - ๐Ÿ‘ค **[Avatars](#avatars)** - User initials or custom avatar images
53
- - ๐ŸŒ **[Localized Dates](#date--time)** - Full i18n support via Day.js
54
- - โŒจ๏ธ **[Keyboard Handling](#keyboard--layout)** - Smart keyboard avoidance for all platforms
55
- - ๐Ÿ’ฌ **[System Messages](#system-messages)** - Display system notifications in chat
56
- - โšก **[Quick Replies](#quick-replies)** - Bot-style quick reply buttons
57
- - ๐Ÿ˜€ **[Emoji Reactions](#emoji-reactions)** - Long-press to react, with reaction pills and an optional full emoji browser
58
- - โœ๏ธ **[Typing Indicator](#typing-indicator)** - Show when users are typing
59
- - โœ… **[Message Status](#message-status)** - Tick indicators for sent/delivered/read states
60
- - โฌ‡๏ธ **[Scroll to Bottom](#scroll-to-bottom)** - Quick navigation button
61
- - ๐ŸŒ **[Web Support](#web-react-native-web)** - Works with react-native-web
62
- - ๐Ÿ“ฑ **[Expo Support](#expo-projects)** - Easy integration with Expo projects
63
- - ๐Ÿ“ **[TypeScript](#typescript)** - Complete TypeScript definitions included
116
+ - ๐ŸŒ— **[Dark mode & theming](#theming--dark-mode)** - full light/dark token system, switchable at runtime
117
+ - ๐Ÿค– **[Streaming AI messages](#streaming-ai-messages)** - token-by-token replies with a typing caret and stop control
118
+ - ๐Ÿ˜€ **[Emoji reactions](#emoji-reactions)** - long-press picker and reaction pills
119
+ - ๐ŸŽฅ **[Video & audio messages](#-whats-new-vs-react-native-gifted-chat)** - real inline playback, with a tappable fallback
120
+ - ๐ŸŽ™๏ธ **[Voice & video recording](#-whats-new-vs-react-native-gifted-chat)** - Telegram-style hold-to-record notes (opt-in)
121
+ - ๐Ÿ“ **[Location messages](#-whats-new-vs-react-native-gifted-chat)** - map card that opens the system maps app
122
+ - โ†ฉ๏ธ **[Swipe to reply](#reply-to-messages)** - reply preview and message threading
123
+ - ๐Ÿ“‹ **[Long-press actions](#message-actions-long-press-context-menu)** - themed context menu anchored to the bubble
124
+ - โšก **[Quick replies](#quick-replies)** - bot-style reply buttons
125
+ - ๐Ÿ”— **[Smart link parsing](#smart-link-parsing)** - URLs, emails, phones, hashtags, mentions
126
+ - ๐Ÿ‘ค **[Avatars](#avatars)** - user initials or custom images
127
+ - โœ๏ธ **[Typing indicator](#typing-indicator)** - show when someone is typing
128
+ - โฎ๏ธ **[Load earlier messages](#load-earlier-messages)** - pagination and infinite scroll
129
+ - โฌ‡๏ธ **[Scroll to bottom](#scroll-to-bottom)** - quick jump back to the latest
130
+ - โœ… **[Message status](#message-status)** - ticks for sent, delivered and read
131
+ - ๐Ÿ’ฌ **[System messages](#system-messages)** - in-line notifications
132
+ - ๐ŸŒ **[Localized dates & i18n](#localization-i18n)** - Day.js dates, translated UI strings
133
+ - โŒจ๏ธ **[Keyboard handling](#keyboard--layout)** - smart avoidance on every platform
134
+ - ๐ŸŽจ **[Fully customizable](#-props-reference)** - override any component with your own
135
+ - ๐Ÿ“ฑ **[Expo](#-installation)** & ๐ŸŒ **[Web](#web-react-native-web)** - works with Expo and react-native-web
136
+ - ๐Ÿ“ **[TypeScript](#typescript)** - complete definitions, generic over your message type
137
+
138
+ Actively maintained, New Architecture ready, built for the latest Reanimated / Gesture Handler.
64
139
 
65
140
  <p align="center">
66
141
  <img width="200" src="https://github.com/user-attachments/assets/c9da88f5-0b20-471c-8cd7-373bdb767517" />
@@ -74,9 +149,10 @@
74
149
 
75
150
  ## ๐Ÿ†• What's new vs react-native-gifted-chat
76
151
 
77
- This library is kept in sync with upstream [`react-native-gifted-chat`](https://github.com/FaridSafi/react-native-gifted-chat)'s latest `master`, so you **keep everything it already has** - New Architecture support, the animated sticky day header, Reanimated 3/4, Gesture Handler, TypeScript - and the same `IMessage` model and prop names. **Moving over is a package swap plus a rename** (see [Migrating](#-migrating-from-react-native-gifted-chat)).
152
+ Kept in sync with upstream [`react-native-gifted-chat`](https://github.com/FaridSafi/react-native-gifted-chat)'s latest `master`, so you **keep everything it already has** - and the same `IMessage` model and prop names. Everything added here is **non-breaking and opt-in**; the media and recording features only activate once you install their optional peer deps.
78
153
 
79
- On top of that, this fork adds the features people kept asking upstream for, plus a modern look out of the box:
154
+ <details>
155
+ <summary><strong>Side-by-side comparison</strong></summary>
80
156
 
81
157
  | Added in this fork | react-native-gifted-chat | @kesha-antonov/react-native-chat |
82
158
  | --- | :---: | :---: |
@@ -92,10 +168,13 @@ On top of that, this fork adds the features people kept asking upstream for, plu
92
168
  | **Location messages** | โŒ ignored | โœ… map card โ†’ opens system maps |
93
169
  | Bubble tails + tighter message grouping | flat bubbles | โœ… |
94
170
 
95
- Everything new is **non-breaking and opt-in**: keep passing the same props you do today and you simply get the modern look and the extra features for free. The media/recording features only activate when you install their optional peer deps and enable them - nobody is forced to add a single dependency.
171
+ </details>
96
172
 
97
173
  ### Theming in one line
98
174
 
175
+ <details>
176
+ <summary><strong>Override any token, light and dark</strong></summary>
177
+
99
178
  ```jsx
100
179
  // Modern defaults out of the box, or override any token (light + dark):
101
180
  <Chat
@@ -105,8 +184,13 @@ Everything new is **non-breaking and opt-in**: keep passing the same props you d
105
184
  />
106
185
  ```
107
186
 
187
+ </details>
188
+
108
189
  ### Voice, video and location
109
190
 
191
+ <details>
192
+ <summary><strong>Opt-in recording and media playback</strong></summary>
193
+
110
194
  ```jsx
111
195
  <Chat
112
196
  audioRecording={{ isEnabled: true }} // hold the mic to record a voice note (needs expo-audio)
@@ -121,9 +205,14 @@ Everything new is **non-breaking and opt-in**: keep passing the same props you d
121
205
  npx expo install expo-video expo-audio expo-image-picker
122
206
  ```
123
207
 
208
+ </details>
209
+
124
210
  ### Custom icons (e.g. Lucide)
125
211
 
126
- Built-in icons are the official [Lucide](https://lucide.dev) glyphs, rendered via the optional `react-native-svg` peer when it is installed, or drawn with `View`s (no dependency) otherwise. Override any of them via the `icons` prop - for example with [`lucide-react-native`](https://lucide.dev/) - and the built-in icon is used for anything you don't override:
212
+ <details>
213
+ <summary><strong>Override any built-in icon</strong></summary>
214
+
215
+ Built-in icons are the official [Lucide](https://lucide.dev) glyphs, rendered via the optional `react-native-svg` peer when it is installed, or drawn with `View`s (no dependency) otherwise. Override any of them via the `icons` prop - the built-in icon is used for anything you don't override:
127
216
 
128
217
  ```tsx
129
218
  import { Send, Mic } from 'lucide-react-native'
@@ -139,6 +228,8 @@ import { Send, Mic } from 'lucide-react-native'
139
228
 
140
229
  Overridable names: `send`, `mic`, `camera`, `play`, `pause`, `check`, `checkAll`, `clock`, `pin`, `plus`, `close`, `chevronLeft`, `chevronDown`, `emoji`, `paperclip`, `reply`, `pencil`, `lock`, `trash`.
141
230
 
231
+ </details>
232
+
142
233
  ---
143
234
 
144
235
  <h3 align="center">Support This Project</h3>
@@ -153,18 +244,35 @@ Overridable names: `send`, `mic`, `camera`, `play`, `pause`, `check`, `checkAll`
153
244
 
154
245
  ---
155
246
 
247
+ > **Using an AI coding agent?** [`llms.txt`](llms.txt) is a condensed integration guide for
248
+ > agents adding this library to an app, and [`AGENTS.md`](AGENTS.md) covers working inside
249
+ > this repository.
250
+
156
251
  ## ๐Ÿ“– Table of Contents
157
252
 
253
+ - [Quick start](#quick-start)
158
254
  - [Features](#-features)
159
255
  - [What's new vs react-native-gifted-chat](#-whats-new-vs-react-native-gifted-chat)
160
256
  - [Requirements](#-requirements)
161
257
  - [Installation](#-installation)
162
258
  - [Migrating from react-native-gifted-chat](#-migrating-from-react-native-gifted-chat)
163
259
  - [Usage](#-usage)
164
- - [Props Reference](#-props-reference)
165
260
  - [Data Structure](#-data-structure)
261
+ - [Props Reference](#-props-reference)
262
+ - [Guides](#-guides)
263
+ - [Reply to Messages](#reply-to-messages)
264
+ - [Maintaining Scroll Position (AI Chatbots)](#maintaining-scroll-position-ai-chatbots)
265
+ - [Streaming (AI) Messages](#streaming-ai-messages)
266
+ - [Emoji Reactions](#emoji-reactions)
267
+ - [Smart Link Parsing](#smart-link-parsing)
268
+ - [Message actions](#message-actions-long-press-context-menu)
269
+ - [Theming & Dark Mode](#theming--dark-mode)
270
+ - [Localization (i18n)](#localization-i18n)
271
+ - [Message Status](#message-status)
272
+ - [TypeScript](#typescript)
166
273
  - [Platform Notes](#-platform-notes)
167
274
  - [Performance](#-performance)
275
+ - [Testing](#-testing)
168
276
  - [Example App](#-example-app)
169
277
  - [Troubleshooting](#-troubleshooting)
170
278
  - [Contributing](#-contributing)
@@ -175,13 +283,9 @@ Overridable names: `send`, `mic`, `camera`, `play`, `pause`, `check`, `checkAll`
175
283
 
176
284
  ## ๐Ÿ“‹ Requirements
177
285
 
178
- | Requirement | Version |
179
- |-------------|---------|
180
- | React Native | >= 0.70.0 |
181
- | iOS | >= 13.4 |
182
- | Android | API 21+ (Android 5.0) |
183
- | Expo | SDK 50+ |
184
- | TypeScript | >= 5.0 (optional) |
286
+ | React Native | iOS | Android | Expo | TypeScript |
287
+ |---|---|---|---|---|
288
+ | >= 0.70.0 | >= 13.4 | API 21+ (5.0) | SDK 50+ | >= 5.0 (optional) |
185
289
 
186
290
  ---
187
291
 
@@ -195,33 +299,37 @@ npx expo install @kesha-antonov/react-native-chat react-native-reanimated react-
195
299
 
196
300
  ### Bare React Native Projects
197
301
 
198
- **Step 1:** Install the packages
302
+ <details>
303
+ <summary><strong>Three steps</strong></summary>
304
+
305
+ **1.** Install the packages:
199
306
 
200
- Using yarn:
201
307
  ```bash
202
308
  yarn add @kesha-antonov/react-native-chat react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
203
309
  ```
204
310
 
205
- Using npm:
206
311
  ```bash
207
312
  npm install --save @kesha-antonov/react-native-chat react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
208
313
  ```
209
314
 
210
- **Step 2:** Install iOS pods
315
+ **2.** Install iOS pods:
211
316
 
212
317
  ```bash
213
318
  npx pod-install
214
319
  ```
215
320
 
216
- **Step 3:** Configure react-native-reanimated
321
+ **3.** Add Reanimated's Babel plugin, following the [react-native-reanimated installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#step-2-add-reanimateds-babel-plugin).
217
322
 
218
- Follow the [react-native-reanimated installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#step-2-add-reanimateds-babel-plugin) to add the Babel plugin.
323
+ </details>
219
324
 
220
325
  ---
221
326
 
222
327
  ## ๐Ÿ”„ Migrating from react-native-gifted-chat
223
328
 
224
- This library is a rebranded continuation of [`react-native-gifted-chat`](https://github.com/FaridSafi/react-native-gifted-chat), built from its latest `master`. **The API, props, and `IMessage` model are unchanged** - migrating is a package swap plus renaming the `GiftedChat*` identifiers.
329
+ **The API, props, and `IMessage` model are unchanged** - migrating is a package swap plus renaming the `GiftedChat*` identifiers. Full guide with a codemod: **[docs/MIGRATION.md](docs/MIGRATION.md)**.
330
+
331
+ <details>
332
+ <summary><strong>What to rename</strong></summary>
225
333
 
226
334
  ```bash
227
335
  yarn remove react-native-gifted-chat
@@ -236,7 +344,7 @@ yarn add @kesha-antonov/react-native-chat
236
344
  | `GiftedChatContext` | `ChatContext` |
237
345
  | `IMessage`, `User`, `useChatContext`, โ€ฆ | unchanged |
238
346
 
239
- See the full guide (codemod included) in **[docs/MIGRATION.md](docs/MIGRATION.md)**.
347
+ </details>
240
348
 
241
349
  ---
242
350
 
@@ -244,47 +352,7 @@ See the full guide (codemod included) in **[docs/MIGRATION.md](docs/MIGRATION.md
244
352
 
245
353
  ### Basic Example
246
354
 
247
- ```jsx
248
- import React, { useState, useCallback, useEffect } from 'react'
249
- import { Chat } from '@kesha-antonov/react-native-chat'
250
-
251
- export function Example() {
252
- const [messages, setMessages] = useState([])
253
-
254
- useEffect(() => {
255
- setMessages([
256
- {
257
- _id: 1,
258
- text: 'Hello developer',
259
- createdAt: new Date(),
260
- user: {
261
- _id: 2,
262
- name: 'John Doe',
263
- avatar: 'https://placeimg.com/140/140/any',
264
- },
265
- },
266
- ])
267
- }, [])
268
-
269
- const onSend = useCallback((messages = []) => {
270
- setMessages(previousMessages =>
271
- Chat.append(previousMessages, messages),
272
- )
273
- }, [])
274
-
275
- return (
276
- <Chat
277
- messages={messages}
278
- onSend={messages => onSend(messages)}
279
- user={{
280
- _id: 1,
281
- }}
282
- />
283
- )
284
- }
285
- ```
286
-
287
- > **๐Ÿ’ก Tip:** Check out more examples in the [`example`](example) directory including Slack-style messages, quick replies, and custom components.
355
+ See [Quick start](#quick-start) above for the minimal setup. More examples - Slack-style messages, quick replies, custom components - live in the [`example`](example) directory.
288
356
 
289
357
  ---
290
358
 
@@ -293,7 +361,7 @@ export function Example() {
293
361
  Messages, system messages, and quick replies follow the structure defined in [Models.ts](src/Models.ts).
294
362
 
295
363
  <details>
296
- <summary><strong>Message Object Structure</strong></summary>
364
+ <summary><strong>Message object structure</strong></summary>
297
365
 
298
366
  ```typescript
299
367
  interface IMessage {
@@ -345,6 +413,11 @@ interface User {
345
413
 
346
414
  ## ๐Ÿ“– Props Reference
347
415
 
416
+ Every group below is collapsed - open the one you need.
417
+
418
+ <details>
419
+ <summary><strong>Core configuration, refs &amp; theming</strong></summary>
420
+
348
421
  ### Core Configuration
349
422
 
350
423
  - **`messages`** _(Array)_ - Messages to display
@@ -363,13 +436,18 @@ interface User {
363
436
  - **`messagesContainerRef`** _(FlatList ref)_ - Ref to the flatlist
364
437
  - **`textInputRef`** _(TextInput ref)_ - Ref to the text input
365
438
 
439
+ </details>
440
+
441
+ <details>
442
+ <summary><strong>Keyboard &amp; layout</strong></summary>
443
+
366
444
  ### Keyboard & Layout
367
445
 
368
446
  - **`keyboardProviderProps`** _(Object)_ - Props to be passed to the [`KeyboardProvider`](https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-provider) for keyboard handling. No defaults are applied - in particular Chat does **not** set `statusBarTranslucent` / `navigationBarTranslucent`, because on Android those change the *activity window* and the change outlives the chat screen ([#2755](https://github.com/FaridSafi/react-native-gifted-chat/issues/2755)). `react-native-keyboard-controller` turns them on by itself when the app is genuinely edge-to-edge, so there is nothing to set in a normal app.
369
447
 
370
448
  Only used when Chat mounts the provider itself. If your app already mounts a `KeyboardProvider` (the setup `react-native-keyboard-controller` recommends - once, at the root), Chat detects it and reuses it instead of nesting a second one, and this prop is ignored. Configure the provider where you mount it.
371
- - **`disableKeyboardProvider`** _(Bool)_ - Skip the built-in `KeyboardProvider` entirely; default is `false`. You do **not** need this just because your app mounts its own provider - that case is detected and reused. Reach for it only to opt out completely, e.g. when the provider's edge-to-edge behavior causes layout shift or a header jump on Android/Expo.
372
- - **`disableGestureHandlerRootView`** _(Bool)_ - Skip the `GestureHandlerRootView` Chat mounts around itself; default is `false`. Unlike `KeyboardProvider`, an existing one can't be auto-detected - `react-native-gesture-handler` doesn't expose that publicly - so set this yourself if your app (or a library it uses, e.g. a bottom sheet) already mounts one at the root. Nesting a second one changes the native view hierarchy around the composer, which has been observed to make a rare Fabric/Yoga layout assertion on iOS more likely, especially when Chat is mounted and unmounted while the keyboard is open ([#17](https://github.com/kesha-antonov/react-native-chat/issues/17)).
449
+ - **`enableKeyboardProvider`** _(Bool)_ - Render the built-in `KeyboardProvider`; default is `true`. You do **not** need to turn this off just because your app mounts its own provider - that case is detected and reused. Set it to `false` only to opt out completely, e.g. when the provider's edge-to-edge behavior causes layout shift or a header jump on Android/Expo.
450
+ - **`enableGestureHandlerRootView`** _(Bool)_ - Render the `GestureHandlerRootView` Chat mounts around itself; default is `true`. Unlike `KeyboardProvider`, an existing one can't be auto-detected - `react-native-gesture-handler` doesn't expose that publicly - so set this to `false` yourself if your app (or a library it uses, e.g. a bottom sheet) already mounts one at the root. Nesting a second one changes the native view hierarchy around the composer, which has been observed to make a rare Fabric/Yoga layout assertion on iOS more likely, especially when Chat is mounted and unmounted while the keyboard is open ([#17](https://github.com/kesha-antonov/react-native-chat/issues/17)).
373
451
  - **`keyboardAvoidingViewProps`** _(Object)_ - Props to be passed to the [`KeyboardAvoidingView`](https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-avoiding-view). See **keyboardVerticalOffset** below for proper keyboard handling.
374
452
  - **`isAlignedTop`** _(`boolean | 'auto'`)_ - Where the bubbles sit **while the whole conversation fits on screen**; once it is taller than the list this has no effect. `false` (default) keeps the usual bottom-anchored chat, `true` pins the messages to the top, and `'auto'` pins them to the top while the keyboard is closed and re-anchors them to the bottom while it is open - so a short conversation starts under the header and moves above the keyboard when the composer is focused ([#2736](https://github.com/FaridSafi/react-native-gifted-chat/issues/2736)). Works with either `isInverted` setting; ignored when `isFlashListEnabled` is set, since FlashList positions its own items.
375
453
  - **`isInverted`** _(Bool)_ - Reverses display order of `messages`; default is `true`
@@ -390,6 +468,11 @@ If you do, sanity-check it on device: a toolbar behind the keyboard means the va
390
468
 
391
469
  > **Upgrading from 4.1.0 or earlier:** the default used to be `insets.top`, which could not account for a navigation header - so most apps passed `useHeaderHeight()` to compensate. That is no longer needed; drop it and let Chat measure, or the toolbar will float above the keyboard by the header height.
392
470
 
471
+ </details>
472
+
473
+ <details>
474
+ <summary><strong>Text input, composer &amp; actions</strong></summary>
475
+
393
476
  ### Text Input & Composer
394
477
 
395
478
  - **`text`** _(String)_ - Input text; default is `undefined`, but if specified, it will override Chat's internal state. Useful for managing text state outside of Chat (e.g. with Redux). Don't forget to implement `textInputProps.onChangeText` to update the text state.
@@ -416,6 +499,11 @@ If you do, sanity-check it on device: a toolbar behind the keyboard means the va
416
499
  - **`actionSheet`** _(Function)_ - Escape hatch for a custom system action sheet. **The bundled `@expo/react-native-action-sheet` dependency was removed**, so `context.actionSheet()` defaults to a no-op; pass your own implementation (with `ActionSheetProvider` in your tree) if you relied on it.
417
500
  - **`actionSheetOptionTintColor`** _(String)_ - Tint color for action labels in the attachment sheet
418
501
 
502
+ </details>
503
+
504
+ <details>
505
+ <summary><strong>Messages, container &amp; bubbles</strong></summary>
506
+
419
507
  ### Messages & Message Container
420
508
 
421
509
  - **`messagesContainerStyle`** _(Object)_ - Custom style for the messages container
@@ -443,20 +531,16 @@ If you do, sanity-check it on device: a toolbar behind the keyboard means the va
443
531
  - **`imageProps`** _(Object)_ - Extra props to be passed to the [`<Image>`](https://reactnative.dev/docs/image) component created by the default `renderMessageImage`
444
532
  - **`imageStyle`** _(Object)_ - Custom style for message images
445
533
  - **`videoProps`** _(Object)_ - Extra props to be passed to the video component created by the required `renderMessageVideo`
446
- - **`messageTextProps`** _(Object)_ - Extra props to be passed to the MessageText component. Useful for customizing link parsing behavior, text styles, and matchers. Supports the following props:
534
+ - **`messageTextProps`** _(Object)_ - Extra props to be passed to the MessageText component. Useful for customizing link parsing behavior, text styles, and matchers:
447
535
  - `matchers` - Custom matchers for linking message content (like URLs, phone numbers, hashtags, mentions)
448
536
  - `linkStyle` - Custom style for links
449
- - `email` - Enable/disable email parsing (default: true)
450
- - `phone` - Enable/disable phone number parsing (default: true)
451
- - `url` - Enable/disable URL parsing (default: true)
452
- - `hashtag` - Enable/disable hashtag parsing (default: false)
453
- - `mention` - Enable/disable mention parsing (default: false)
454
- - `hashtagUrl` - Base URL for hashtags (e.g., 'https://x.com/hashtag')
455
- - `mentionUrl` - Base URL for mentions (e.g., 'https://x.com')
537
+ - `email` / `phone` / `url` - Enable/disable parsing (default: true)
538
+ - `hashtag` / `mention` - Enable/disable parsing (default: false)
539
+ - `hashtagUrl` / `mentionUrl` - Base URLs (e.g. `'https://x.com/hashtag'`)
456
540
  - `stripPrefix` - Strip 'http://' or 'https://' from URL display (default: false)
457
541
  - `TextComponent` - Custom Text component to use (e.g., from react-native-gesture-handler)
458
542
 
459
- Example:
543
+ A custom matcher, replacing the default phone linking with an action sheet:
460
544
 
461
545
  ```tsx
462
546
  <Chat
@@ -504,7 +588,12 @@ Example:
504
588
  />
505
589
  ```
506
590
 
507
- See full example in [LinksExample](example/components/chat-examples/LinksExample.tsx)
591
+ See the full example in [LinksExample](example/components/chat-examples/LinksExample.tsx).
592
+
593
+ </details>
594
+
595
+ <details>
596
+ <summary><strong>Avatars, username, date &amp; time, system messages</strong></summary>
508
597
 
509
598
  ### Avatars
510
599
 
@@ -546,17 +635,20 @@ See full example in [LinksExample](example/components/chat-examples/LinksExample
546
635
 
547
636
  - **`renderSystemMessage`** _(Component | Function)_ - Custom system message
548
637
 
638
+ </details>
639
+
640
+ <details>
641
+ <summary><strong>Load earlier, typing indicator, quick replies, scroll to bottom</strong></summary>
642
+
549
643
  ### Load Earlier Messages
550
644
 
551
- - **`loadEarlierMessagesProps`** _(Object)_ - Props to pass to the LoadEarlierMessages component. The button is only visible when `isAvailable` is `true`. Supports the following props:
645
+ - **`loadEarlierMessagesProps`** _(Object)_ - Props to pass to the LoadEarlierMessages component. The button is only visible when `isAvailable` is `true`:
552
646
  - `isAvailable` - Controls button visibility (default: false)
553
647
  - `onPress` - Callback when button is pressed
554
648
  - `isLoading` - Display loading indicator (default: false)
555
649
  - `isInfiniteScrollEnabled` - Enable infinite scroll up when reaching the top of messages container, automatically calls `onPress` (not yet supported for web)
556
650
  - `label` - Override the default "Load earlier messages" text
557
- - `containerStyle` - Custom style for the button container
558
- - `wrapperStyle` - Custom style for the button wrapper
559
- - `textStyle` - Custom style for the button text
651
+ - `containerStyle` / `wrapperStyle` / `textStyle` - Custom styles for the button
560
652
  - `activityIndicatorStyle` - Custom style for the loading indicator
561
653
  - `activityIndicatorColor` - Color of the loading indicator (default: 'white')
562
654
  - `activityIndicatorSize` - Size of the loading indicator (default: 'small')
@@ -564,14 +656,14 @@ See full example in [LinksExample](example/components/chat-examples/LinksExample
564
656
 
565
657
  ### Typing Indicator
566
658
 
567
- - **`isTyping`** _(Bool)_ - Typing Indicator state; default `false`. If you use`renderFooter` it will override this.
659
+ - **`isTyping`** _(Bool)_ - Typing Indicator state; default `false`. If you use `renderFooter` it will override this.
568
660
  - **`renderTypingIndicator`** _(Component | Function)_ - Custom typing indicator component
569
661
  - **`typingIndicatorStyle`** _(StyleProp<ViewStyle>)_ - Custom style for the TypingIndicator component.
570
- - **`renderFooter`** _(Component | Function)_ - Custom footer component on the ListView, e.g. `'User is typing...'`; see [CustomizedFeaturesExample.tsx](example/components/chat-examples/CustomizedFeaturesExample.tsx) for an example. Overrides default typing indicator that triggers when `isTyping` is true.
662
+ - **`renderFooter`** _(Component | Function)_ - Custom footer component on the ListView, e.g. `'User is typing...'`; see [CustomizedRenderingExample.tsx](example/components/chat-examples/CustomizedRenderingExample.tsx) for an example. Overrides default typing indicator that triggers when `isTyping` is true.
571
663
 
572
664
  ### Quick Replies
573
665
 
574
- See [Quick Replies example in messages.ts](example/example-expo/data/messages.ts)
666
+ See the [quick replies example in messages.ts](example/example-expo/data/messages.ts).
575
667
 
576
668
  - **`onQuickReply`** _(Function)_ - Callback when sending a quick reply (to backend server)
577
669
  - **`renderQuickReplies`** _(Function)_ - Custom all quick reply view
@@ -580,11 +672,26 @@ See [Quick Replies example in messages.ts](example/example-expo/data/messages.ts
580
672
  - **`quickReplyContainerStyle`** _(StyleProp<ViewStyle>)_ - Custom container style for quick replies
581
673
  - **`renderQuickReplySend`** _(Function)_ - Custom quick reply **send** view
582
674
 
675
+ ### Scroll to Bottom
676
+
677
+ - **`isScrollToBottomEnabled`** _(Bool)_ - Enables the scroll to bottom Component (Default is false)
678
+ - **`scrollToBottomComponent`** _(Function)_ - Custom Scroll To Bottom Component container
679
+ - **`scrollToBottomOffset`** _(Integer)_ - Custom Height Offset upon which to begin showing Scroll To Bottom Component (Default is 200)
680
+ - **`scrollToBottomStyle`** _(Object)_ - Custom style for Scroll To Bottom wrapper (position, bottom, right, etc.)
681
+ - **`scrollToBottomContentStyle`** _(Object)_ - Custom style for Scroll To Bottom content (size, background, shadow, etc.)
682
+
683
+ </details>
684
+
685
+ ---
686
+
687
+ ## ๐Ÿงฉ Guides
688
+
583
689
  ### Reply to Messages
584
690
 
585
- React Native Chat supports swipe-to-reply functionality out of the box. When enabled, users can swipe on a message to reply to it, displaying a reply preview in the input toolbar and the replied message above the new message bubble.
691
+ <details>
692
+ <summary><strong>Swipe-to-reply, reply preview and threading</strong></summary>
586
693
 
587
- > **Note:** This feature uses `ReanimatedSwipeable` from `react-native-gesture-handler` and `react-native-reanimated` for smooth, performant animations.
694
+ Swipe on a message to reply to it, showing a reply preview in the input toolbar and the replied message above the new bubble. Uses `ReanimatedSwipeable` from `react-native-gesture-handler` plus `react-native-reanimated`.
588
695
 
589
696
  #### Basic Usage
590
697
 
@@ -604,8 +711,6 @@ React Native Chat supports swipe-to-reply functionality out of the box. When ena
604
711
 
605
712
  #### Reply Props (Grouped)
606
713
 
607
- The `reply` prop accepts an object with the following structure:
608
-
609
714
  ```typescript
610
715
  interface ReplyProps<TMessage> {
611
716
  // Swipe gesture configuration
@@ -652,8 +757,6 @@ interface ReplyProps<TMessage> {
652
757
 
653
758
  #### ReplyMessage Structure
654
759
 
655
- When a message has a reply, it includes a `replyMessage` property:
656
-
657
760
  ```typescript
658
761
  interface ReplyMessage {
659
762
  _id: string | number
@@ -695,23 +798,15 @@ const [replyMessage, setReplyMessage] = useState<ReplyMessage | null>(null)
695
798
 
696
799
  #### Smooth Animations
697
800
 
698
- The reply preview automatically animates when:
699
- - **Appearing**: Smoothly expands from zero height with fade-in effect
700
- - **Disappearing**: Smoothly collapses with fade-out effect
701
- - **Content changes**: Smoothly transitions when replying to a different message
702
-
703
- These animations use `react-native-reanimated` for 60fps performance.
801
+ The reply preview animates automatically - it expands from zero height with a fade-in when it appears, collapses with a fade-out when cleared, and transitions smoothly when you reply to a different message. These use `react-native-reanimated` for 60fps performance.
704
802
 
705
- ### Scroll to Bottom
706
-
707
- - **`isScrollToBottomEnabled`** _(Bool)_ - Enables the scroll to bottom Component (Default is false)
708
- - **`scrollToBottomComponent`** _(Function)_ - Custom Scroll To Bottom Component container
709
- - **`scrollToBottomOffset`** _(Integer)_ - Custom Height Offset upon which to begin showing Scroll To Bottom Component (Default is 200)
710
- - **`scrollToBottomStyle`** _(Object)_ - Custom style for Scroll To Bottom wrapper (position, bottom, right, etc.)
711
- - **`scrollToBottomContentStyle`** _(Object)_ - Custom style for Scroll To Bottom content (size, background, shadow, etc.)
803
+ </details>
712
804
 
713
805
  ### Maintaining Scroll Position (AI Chatbots)
714
806
 
807
+ <details>
808
+ <summary><strong>Keep the reading position while long answers stream in</strong></summary>
809
+
715
810
  For AI chat interfaces where long responses arrive and you don't want to disrupt the user's reading position, use [`maintainVisibleContentPosition`](https://reactnative.dev/docs/scrollview#maintainvisiblecontentposition) via `listProps`:
716
811
 
717
812
  ```tsx
@@ -749,15 +844,20 @@ const [isScrolledUp, setIsScrolledUp] = useState(false)
749
844
  />
750
845
  ```
751
846
 
847
+ </details>
848
+
752
849
  ### Streaming (AI) Messages
753
850
 
754
- Render AI assistant replies token-by-token. The library batches incoming chunks with `requestAnimationFrame` (one render per frame, only the streaming bubble re-renders) and shows a blinking caret while a message is still streaming.
851
+ Render AI assistant replies token-by-token. Incoming chunks are batched with `requestAnimationFrame` (one render per frame, only the streaming bubble re-renders) and a blinking caret shows while a message is still streaming.
755
852
 
756
853
  <p align="center">
757
854
  <img width="250" src="https://raw.githubusercontent.com/kesha-antonov/react-native-chat/main/media/ai-streaming-markdown.png" alt="AI reply streaming token-by-token and rendering as markdown, with a blinking caret and a Stop button in the composer" />
758
855
  </p>
759
856
 
760
- > The reply above streams in token-by-token (note the caret `โ–‹`) and renders as **markdown** - bold, italics, lists, inline and fenced code - while the composer's send button turns into a **Stop** control mid-stream. See [Markdown rendering for AI replies](#markdown-rendering-for-ai-replies) to enable markdown.
857
+ <details>
858
+ <summary><strong>useStreamingMessages, stop control and a full example</strong></summary>
859
+
860
+ The reply above streams in token-by-token (note the caret `โ–‹`) and renders as **markdown** - bold, italics, lists, inline and fenced code - while the composer's send button turns into a **Stop** control mid-stream. See [Markdown rendering for AI replies](#markdown-rendering-for-ai-replies) to enable markdown.
761
861
 
762
862
  - **`IMessage.streaming`** - flag a message as streaming (shows the caret)
763
863
  - **`useStreamingMessages(...)`** - owns the message list, rAF-batches `push()`, and supports stop via `AbortController`. It returns `{ messages, setMessages, append, startStream, isStreaming, stop }`; `setMessages` is there for anything the hook does not cover, so you can patch a message with a plain `map`.
@@ -786,8 +886,13 @@ function Bot () {
786
886
 
787
887
  See **[docs/STREAMING.md](./docs/STREAMING.md)** for the full hook API and a real Claude streaming adapter (via a backend proxy). A runnable demo lives in `example/components/chat-examples/AIBotExample.tsx`.
788
888
 
889
+ </details>
890
+
789
891
  #### Markdown rendering for AI replies
790
892
 
893
+ <details>
894
+ <summary><strong>Built-in renderer, or react-native-streamdown</strong></summary>
895
+
791
896
  AI/LLM replies are usually markdown (bold, lists, fenced code). Enable markdown with `messageTextProps={{ markdown: true }}` (streamed messages auto-render as markdown unless you pass `markdown={false}`):
792
897
 
793
898
  ```tsx
@@ -813,9 +918,11 @@ There are two renderers and you get the best available one automatically:
813
918
  <Chat messageTextProps={{ markdown: true, markdownProps: { /* ... */ } }} {...props} />
814
919
  ```
815
920
 
921
+ </details>
922
+
816
923
  ### Emoji Reactions
817
924
 
818
- Long-press a message to open a quick emoji picker; selected reactions render as pills below the bubble and toggle on tap. The core ships a lightweight quick picker (built on `react-native-gesture-handler` and `react-native-reanimated`, no extra dependencies). A full emoji browser is optional and demonstrated in the example app via the `renderReactionPicker` override.
925
+ Long-press a message to open a quick emoji picker; selected reactions render as pills below the bubble and toggle on tap. The quick picker ships in the core with no extra dependencies; a full emoji browser is optional via the `renderReactionPicker` override.
819
926
 
820
927
  <p align="center">
821
928
  <img width="200" src="https://raw.githubusercontent.com/kesha-antonov/react-native-chat/main/media/reactions-picker.png" />
@@ -825,6 +932,9 @@ Long-press a message to open a quick emoji picker; selected reactions render as
825
932
  <img width="200" src="https://raw.githubusercontent.com/kesha-antonov/react-native-chat/main/media/reactions-emoji-browser.png" />
826
933
  </p>
827
934
 
935
+ <details>
936
+ <summary><strong>Wiring up the toggle, and the full prop list</strong></summary>
937
+
828
938
  Store reactions on each message as a `reactions` array, then enable the feature and handle the toggle. Reaction state is owned by you, so it works with any backend:
829
939
 
830
940
  ```tsx
@@ -874,8 +984,6 @@ const handleReactionPress = useCallback((message: IChatMessage, emoji: string) =
874
984
 
875
985
  #### Reactions Props (Grouped)
876
986
 
877
- The `reactions` prop accepts:
878
-
879
987
  - **`isEnabled`** _(Bool)_ - Enable emoji reactions (default `false`)
880
988
  - **`emojis`** _(String[])_ - Emojis shown in the quick picker (default `['๐Ÿ‘', 'โค๏ธ', '๐Ÿ˜‚', '๐Ÿ˜ฎ', '๐Ÿ˜ข', '๐Ÿ‘Ž']`)
881
989
  - **`onReactionPress`** _(Function)_ - `(message, emoji) => void` called when an emoji is selected or a pill is tapped. Toggle logic is left to you
@@ -884,8 +992,13 @@ The `reactions` prop accepts:
884
992
  - **`containerStyle`**, **`reactionStyle`**, **`reactionActiveStyle`**, **`reactionTextStyle`**, **`reactionCountStyle`** - Styles for the reaction pills
885
993
  - **`pickerContainerStyle`**, **`pickerEmojiStyle`** - Styles for the quick picker
886
994
 
995
+ </details>
996
+
887
997
  ### Smart Link Parsing
888
998
 
999
+ <details>
1000
+ <summary><strong>URLs, emails, phones, hashtags and mentions</strong></summary>
1001
+
889
1002
  Message text is automatically scanned for URLs, emails, and phone numbers; hashtags and mentions are opt-in. Configure it via `messageTextProps`:
890
1003
 
891
1004
  ```tsx
@@ -909,9 +1022,14 @@ Message text is automatically scanned for URLs, emails, and phone numbers; hasht
909
1022
 
910
1023
  For full control, pass custom `matchers` (`{ type, pattern, getLinkUrl?, getLinkText?, renderLink?, onPress? }[]`) to add or override patterns. See the Links example in the [example app](#-example-app).
911
1024
 
1025
+ </details>
1026
+
912
1027
  ### Message actions (long-press context menu)
913
1028
 
914
- Long-press a message to open a floating, themed **context menu** (Telegram style) anchored to the bubble. Provide the actions via `messageActions` - an array, or a function of the message - each `{ label, icon?, onPress, destructive? }`. When reactions are enabled, a reactions row is shown on top of the menu automatically.
1029
+ <details>
1030
+ <summary><strong>Telegram-style floating menu</strong></summary>
1031
+
1032
+ Long-press a message to open a floating, themed context menu anchored to the bubble. Provide the actions via `messageActions` - an array, or a function of the message - each `{ label, icon?, onPress, destructive? }`. When reactions are enabled, a reactions row is shown on top of the menu automatically.
915
1033
 
916
1034
  ```tsx
917
1035
  import { setStringAsync } from 'expo-clipboard'
@@ -925,8 +1043,15 @@ import { Copy, Trash2 } from 'lucide-react-native' // optional icons
925
1043
  />
926
1044
  ```
927
1045
 
1046
+ > **Note:** This library no longer depends on `@expo/react-native-action-sheet`. Prefer `messageActions` above. If you specifically want a native action sheet, install it yourself, wrap your tree in `ActionSheetProvider`, and either call `useActionSheet()` in your own `onLongPressMessage` or pass an `actionSheet` prop - the `actionSheet` prop / `context.actionSheet()` escape hatch still works when you provide an implementation. The composer "+" actions use the built-in themed `AttachmentSheet` and need no setup.
1047
+
1048
+ </details>
1049
+
928
1050
  ### Interactive content inside bubbles (video players, maps)
929
1051
 
1052
+ <details>
1053
+ <summary><strong>Keeping native controls tappable</strong></summary>
1054
+
930
1055
  When `reactions` or `messageActions` are enabled, the long-press surface spans the **whole message row** - the bubble *and* the empty space beside it, the way Telegram behaves on Android. (A tap gesture is added on top only when `onPressMessage` is set.) Those recognizers do not cancel touches on native subviews, so a `react-native-video` / `expo-video` player rendered through `renderMessageVideo` keeps its native `controls` interactive.
931
1056
 
932
1057
  If a message must own every touch that lands on it, set `isMessageGestureEnabled` to `false` for it. The gesture surface then drops *behind* the bubble: the bubble's content takes its touches, and long-pressing the row next to the bubble still opens the picker - so reactions are never lost for that message.
@@ -940,10 +1065,13 @@ If a message must own every touch that lands on it, set `isMessageGestureEnabled
940
1065
  />
941
1066
  ```
942
1067
 
943
- > **Note:** This library no longer depends on `@expo/react-native-action-sheet`. Prefer `messageActions` above. If you specifically want a native action sheet, install it yourself, wrap your tree in `ActionSheetProvider`, and either call `useActionSheet()` in your own `onLongPressMessage` or pass an `actionSheet` prop - the `actionSheet` prop / `context.actionSheet()` escape hatch still works when you provide an implementation. The composer "+" actions use the built-in themed `AttachmentSheet` and need no setup.
1068
+ </details>
944
1069
 
945
1070
  ### Theming & Dark Mode
946
1071
 
1072
+ <details>
1073
+ <summary><strong>Token groups, runtime switching and themed components</strong></summary>
1074
+
947
1075
  The chat ships with a modern default look and a full token-based theme. Override any subset of tokens via `theme` (light) and `darkTheme` (dark); your overrides are deep-merged over `defaultLightTheme` / `defaultDarkTheme`, and the resolved theme switches at runtime with the color scheme (system, or forced via `colorScheme`). Explicit per-component style props still win over the theme.
948
1076
 
949
1077
  ```tsx
@@ -975,9 +1103,14 @@ const MyBadge = () => {
975
1103
 
976
1104
  Also exported: `defaultLightTheme`, `defaultDarkTheme`, and the `ChatTheme` / `PartialChatTheme` types.
977
1105
 
1106
+ </details>
1107
+
978
1108
  ### Localization (i18n)
979
1109
 
980
- All built-in UI strings (composer placeholder, send/cancel, load earlier, today, voice/video/location labels, slide-to-cancel, reply/edit banner, camera-permission text) route through a label table. Built-in translations ship for `en`, `es`, `fr`, `de`, and `ru`, selected by the existing `locale` prop. Override any individual string with `labels`:
1110
+ <details>
1111
+ <summary><strong>Built-in translations and label overrides</strong></summary>
1112
+
1113
+ All built-in UI strings (composer placeholder, send/cancel, load earlier, today, voice/video/location labels, slide-to-cancel, reply/edit banner, camera-permission text) route through a label table. Built-in translations ship for `es`, `fr`, `de`, `ru`, `zh`, `ar`, `pt`, `ja`, `ko`, `it`, `tr`, `hi`, `nl`, `pl` and `id`, with English as the default, selected by the existing `locale` prop. A regional tag falls back to its base language, so `pt-BR` resolves to `pt`. Override any individual string with `labels`:
981
1114
 
982
1115
  ```tsx
983
1116
  <Chat
@@ -989,8 +1122,13 @@ All built-in UI strings (composer placeholder, send/cancel, load earlier, today,
989
1122
 
990
1123
  Exported helpers: `ChatLabels` (type), `defaultLabels`, `translations`, `resolveLabels`, and the `useLabels` hook for reading the resolved labels in custom components.
991
1124
 
1125
+ </details>
1126
+
992
1127
  ### Message Status
993
1128
 
1129
+ <details>
1130
+ <summary><strong>Tick indicators for sent / delivered / read</strong></summary>
1131
+
994
1132
  Set `sent`, `received`, or `pending` on a message to show its delivery status. By default these render as tick indicators next to the timestamp (`โœ“` sent, `โœ“โœ“` received, `๐Ÿ•“` pending):
995
1133
 
996
1134
  ```tsx
@@ -1013,8 +1151,13 @@ Customize the indicators with `renderTicks` (full override) or `tickStyle` (styl
1013
1151
  />
1014
1152
  ```
1015
1153
 
1154
+ </details>
1155
+
1016
1156
  ### TypeScript
1017
1157
 
1158
+ <details>
1159
+ <summary><strong>Generic over your own message type</strong></summary>
1160
+
1018
1161
  Chat ships complete type definitions and is generic over your message type. Extend `IMessage` to add custom fields and everything stays typed end to end:
1019
1162
 
1020
1163
  ```tsx
@@ -1031,8 +1174,7 @@ interface MyMessage extends IMessage {
1031
1174
  />
1032
1175
  ```
1033
1176
 
1034
- ---
1035
-
1177
+ </details>
1036
1178
 
1037
1179
  ---
1038
1180
 
@@ -1068,43 +1210,68 @@ For **Expo**, you can append `KeyboardAvoidingView` after Chat (Android only):
1068
1210
 
1069
1211
  ### Web (react-native-web)
1070
1212
 
1213
+ Install the web renderer alongside the library:
1214
+
1215
+ ```bash
1216
+ yarn add react-native-web react-dom
1217
+ ```
1218
+
1071
1219
  <details>
1072
- <summary><strong>With create-react-app</strong></summary>
1073
-
1074
- 1. Install react-app-rewired: `yarn add -D react-app-rewired`
1075
- 2. Create `config-overrides.js`:
1076
-
1077
- ```js
1078
- module.exports = function override(config, env) {
1079
- config.module.rules.push({
1080
- test: /\.js$/,
1081
- exclude: /node_modules[/\\](?!react-native-chat)/,
1082
- use: {
1083
- loader: 'babel-loader',
1084
- options: {
1085
- babelrc: false,
1086
- configFile: false,
1087
- presets: [
1088
- ['@babel/preset-env', { useBuiltIns: 'usage' }],
1089
- '@babel/preset-react',
1090
- ],
1091
- plugins: ['@babel/plugin-proposal-class-properties'],
1092
- },
1093
- },
1094
- })
1095
- return config
1096
- }
1220
+ <summary><strong>Expo / Metro (recommended) - no extra config</strong></summary>
1221
+
1222
+ ```bash
1223
+ npx expo start --web
1097
1224
  ```
1098
1225
 
1226
+ Nothing else to set up. Metro runs Babel over `node_modules`, so this package's JSX and its Reanimated worklets get transpiled the same way your own code does. The [example app](#-example-app) runs on web exactly like this.
1227
+
1228
+ </details>
1229
+
1230
+ <details>
1231
+ <summary><strong>Other bundlers (webpack, Vite, Next.js) - the package must be transpiled</strong></summary>
1232
+
1233
+ The published files are ES modules with JSX left in place, and the component uses Reanimated worklets, which need `react-native-reanimated/plugin` to run. Bundlers that skip `node_modules` when transpiling - webpack's `babel-loader` does by default - will choke on that. So outside Metro you need to:
1234
+
1235
+ 1. Alias `react-native` to `react-native-web` and let the resolver pick up `.web.js` / `.web.tsx` extensions
1236
+ 2. Run `@kesha-antonov/react-native-chat` - and the React Native packages it depends on - through Babel with `@react-native/babel-preset` and `react-native-reanimated/plugin`, instead of excluding all of `node_modules`
1237
+
1238
+ In Next.js that is `transpilePackages` in `next.config.js`; in a plain webpack setup it means widening the `babel-loader` rule's `exclude` so these packages are included.
1239
+
1240
+ </details>
1241
+
1242
+ ### React Strict DOM
1243
+
1244
+ <details>
1245
+ <summary><strong>Works inside an RSD tree - verified on iOS and Expo web</strong></summary>
1246
+
1247
+ Chat drops into a [React Strict DOM](https://github.com/facebook/react-strict-dom) app unchanged. RSD's native build renders through React Native, so `<Chat>` is just another RN subtree:
1248
+
1249
+ ```tsx
1250
+ import { css, html } from 'react-strict-dom'
1251
+ import { Chat } from '@kesha-antonov/react-native-chat'
1252
+
1253
+ const styles = css.create({
1254
+ root: { display: 'flex', flexDirection: 'column', height: '100%' },
1255
+ })
1256
+
1257
+ <html.div data-layoutconformance='strict' style={styles.root}>
1258
+ <Chat messages={messages} onSend={onSend} user={user} />
1259
+ </html.div>
1260
+ ```
1261
+
1262
+ Three things to know:
1263
+
1264
+ - **Web needs `react-native-web`.** RSD's web build contains no React Native code at all, so Chat does not resolve there on its own. Expo web gives you `react-native-web` already, so RSD (real DOM) and Chat (RNW's DOM output) render side by side in one tree. A Vite or Next.js RSD app has to add the alias and transpile step from the section above
1265
+ - **Metro needs `unstable_enablePackageExports: true`.** RSD publishes only an `exports` map with no `main`, so with package exports off it fails to resolve. This is the default on recent Expo SDKs
1266
+ - **StyleX styles do not reach Chat.** It takes React Native style objects and its own `theme` prop, not `css.create` output. On native, `compat.native` is the bridge if you want RSD to drive a wrapper around it
1267
+
1099
1268
  </details>
1100
1269
 
1101
1270
  ---
1102
1271
 
1103
1272
  ## โšก Performance
1104
1273
 
1105
- The chat is built for long lists, but a few habits on your side unlock the most:
1106
-
1107
- **Memoize your render props and config.** Each message row is wrapped in `React.memo` with a comparator that deep-compares the message and reference-compares every other prop. So an unchanged row only skips a re-render when the props you pass it are referentially stable. If you pass inline functions or objects, that row re-renders on every parent render:
1274
+ The chat is built for long lists. The one habit that matters most: **memoize your render props and config**, because each message row is `React.memo`'d with a comparator that deep-compares the message and reference-compares everything else - so an inline function or object re-renders that row on every parent render.
1108
1275
 
1109
1276
  ```jsx
1110
1277
  // โŒ New reference every render - the row can't skip
@@ -1113,12 +1280,14 @@ The chat is built for long lists, but a few habits on your side unlock the most:
1113
1280
  // โœ… Stable references - unchanged rows skip re-renders
1114
1281
  const renderBubble = useCallback(props => <MyBubble {...props} />, [])
1115
1282
  const reactions = useMemo(() => ({ isEnabled: true, onReactionPress }), [onReactionPress])
1116
- const messageActions = useCallback(message => [{ label: 'Copy', onPress: () => copy(message.text) }], [copy])
1117
1283
 
1118
- <Chat renderBubble={renderBubble} reactions={reactions} messageActions={messageActions} />
1284
+ <Chat renderBubble={renderBubble} reactions={reactions} />
1119
1285
  ```
1120
1286
 
1121
- This applies to all render props (`renderBubble`, `renderMessageText`, `renderAvatar`, ...), the `reactions` / `audioRecording` / `videoRecording` / `messageActions` config objects, and any style objects.
1287
+ <details>
1288
+ <summary><strong>Immutability, context props and virtualization tuning</strong></summary>
1289
+
1290
+ The memoization advice applies to all render props (`renderBubble`, `renderMessageText`, `renderAvatar`, ...), the `reactions` / `audioRecording` / `videoRecording` / `messageActions` config objects, and any style objects.
1122
1291
 
1123
1292
  **Keep messages immutable.** Update messages by creating new arrays/objects (e.g. `Chat.append(...)`), never by mutating an existing message in place - the row comparator relies on value changes to detect updates.
1124
1293
 
@@ -1130,8 +1299,13 @@ This applies to all render props (`renderBubble`, `renderMessageText`, `renderAv
1130
1299
  <Chat listProps={{ windowSize: 7, removeClippedSubviews: true }} {...props} />
1131
1300
  ```
1132
1301
 
1302
+ </details>
1303
+
1133
1304
  ### FlashList (opt-in)
1134
1305
 
1306
+ <details>
1307
+ <summary><strong>Recycling rows on very long histories</strong></summary>
1308
+
1135
1309
  On long histories `FlatList` can log `VirtualizedList: You have a large list that is slow to update`. [FlashList](https://shopify.github.io/flash-list/) v2 recycles rows instead of keeping them mounted, which removes that class of stall. It is supported as an **optional** dependency - install it yourself and flip one prop:
1136
1310
 
1137
1311
  ```bash
@@ -1165,6 +1339,8 @@ Notes:
1165
1339
  - `FlatList`-only knobs (`windowSize`, `maxToRenderPerBatch`, `initialNumToRender`, `updateCellsBatchingPeriod`, `removeClippedSubviews`) are not forwarded to FlashList - it sizes its own render window.
1166
1340
  - If `@shopify/flash-list` is not installed, the prop is ignored, a warning is logged, and `FlatList` is used.
1167
1341
 
1342
+ </details>
1343
+
1168
1344
  ---
1169
1345
 
1170
1346
  ## ๐Ÿงช Testing
@@ -1197,33 +1373,22 @@ fireEvent(loadingWrapper, 'layout', {
1197
1373
 
1198
1374
  ## ๐Ÿ“ฆ Example App
1199
1375
 
1200
- The repository includes a comprehensive example app demonstrating all features:
1376
+ <details>
1377
+ <summary><strong>Running the demo locally</strong></summary>
1378
+
1379
+ The repository includes a comprehensive example app demonstrating all features - basic chat, custom bubbles and avatars, swipe-to-reply, quick replies, typing indicators, attachment actions, link parsing with custom matchers, and web compatibility.
1201
1380
 
1202
1381
  ```bash
1203
- # Clone and install
1204
1382
  git clone https://github.com/kesha-antonov/react-native-chat.git
1205
1383
  cd react-native-chat/example
1206
1384
  yarn install
1207
1385
 
1208
- # Run on iOS
1209
- npx expo run:ios
1210
-
1211
- # Run on Android
1212
- npx expo run:android
1213
-
1214
- # Run on Web
1215
- npx expo start --web
1386
+ npx expo run:ios # iOS
1387
+ npx expo run:android # Android
1388
+ npx expo start --web # Web
1216
1389
  ```
1217
1390
 
1218
- The example app showcases:
1219
- - ๐Ÿ’ฌ Basic chat functionality
1220
- - ๐ŸŽจ Custom message bubbles and avatars
1221
- - โ†ฉ๏ธ Reply to messages with swipe gesture
1222
- - โšก Quick replies (bot-style)
1223
- - โœ๏ธ Typing indicators
1224
- - ๐Ÿ“Ž Attachment actions
1225
- - ๐Ÿ”— Link parsing and custom matchers
1226
- - ๐ŸŒ Web compatibility
1391
+ </details>
1227
1392
 
1228
1393
  ---
1229
1394
 
@@ -1277,37 +1442,24 @@ See [this issue](https://github.com/kesha-antonov/react-native-chat/issues/298)
1277
1442
 
1278
1443
  ## ๐Ÿค Contributing
1279
1444
 
1280
- Contributions are welcome! Please feel free to submit a Pull Request.
1281
-
1282
- 1. Fork the repository
1283
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
1284
- 3. Install dependencies (`yarn install`)
1285
- 4. Make your changes
1286
- 5. Run tests (`yarn test`)
1287
- 6. Run linting (`yarn lint`)
1288
- 7. Build the library (`yarn build`)
1289
- 8. Commit your changes (`git commit -m 'Add amazing feature'`)
1290
- 9. Push to the branch (`git push origin feature/amazing-feature`)
1291
- 10. Open a Pull Request
1445
+ Contributions are welcome - fork, branch, and open a Pull Request.
1292
1446
 
1293
1447
  ### Development Setup
1294
1448
 
1295
- ```bash
1296
- # Install dependencies
1297
- yarn install
1298
-
1299
- # Build the library
1300
- yarn build
1449
+ <details>
1450
+ <summary><strong>Commands</strong></summary>
1301
1451
 
1302
- # Run tests
1303
- yarn test
1452
+ ```bash
1453
+ yarn install # install dependencies
1454
+ yarn build # build the library
1455
+ yarn test # run tests
1456
+ yarn lint # run linting
1457
+ yarn prepublishOnly # full validation
1458
+ ```
1304
1459
 
1305
- # Run linting
1306
- yarn lint
1460
+ Before opening a PR, please make sure `yarn test`, `yarn lint` and `yarn build` all pass.
1307
1461
 
1308
- # Full validation
1309
- yarn prepublishOnly
1310
- ```
1462
+ </details>
1311
1463
 
1312
1464
  ---
1313
1465
 
@@ -1317,6 +1469,8 @@ Based on [FaridSafi/react-native-gifted-chat](https://github.com/FaridSafi/react
1317
1469
 
1318
1470
  **Maintainer:** [Kesha Antonov](https://github.com/kesha-antonov)
1319
1471
 
1472
+ I also build **[cryptoc](https://cryptoc-app.web.app/)** - a crypto portfolio app with home screen, lock screen and Watch widgets, no account and no exchange API keys.
1473
+
1320
1474
  > I maintained the original project solo for 2 years before deciding to continue development in this repository. If you find this library useful, please consider [becoming a sponsor](https://github.com/sponsors/kesha-antonov) to support continued development. ๐Ÿ’–
1321
1475
 
1322
1476
  ---