@kesha-antonov/react-native-chat 4.4.0 → 5.0.0

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 (47) hide show
  1. package/README.md +318 -211
  2. package/lib/Bubble/index.d.ts.map +1 -1
  3. package/lib/Bubble/index.js +1 -0
  4. package/lib/Bubble/index.js.map +1 -1
  5. package/lib/Chat/index.d.ts.map +1 -1
  6. package/lib/Chat/index.js +36 -15
  7. package/lib/Chat/index.js.map +1 -1
  8. package/lib/Chat/types.d.ts +12 -11
  9. package/lib/Chat/types.d.ts.map +1 -1
  10. package/lib/InputToolbar.d.ts.map +1 -1
  11. package/lib/InputToolbar.js +3 -0
  12. package/lib/InputToolbar.js.map +1 -1
  13. package/lib/MessageImage.js +2 -2
  14. package/lib/MessageImage.js.map +1 -1
  15. package/lib/MessagesContainer/components/DayAnimated/debug.d.ts +1 -1
  16. package/lib/MessagesContainer/components/DayAnimated/debug.d.ts.map +1 -1
  17. package/lib/MessagesContainer/components/DayAnimated/debug.js +2 -2
  18. package/lib/MessagesContainer/components/DayAnimated/debug.js.map +1 -1
  19. package/lib/MessagesContainer/components/DayAnimated/index.d.ts.map +1 -1
  20. package/lib/MessagesContainer/components/DayAnimated/index.js +5 -5
  21. package/lib/MessagesContainer/components/DayAnimated/index.js.map +1 -1
  22. package/lib/MessagesContainer/components/DayAnimated/useScrollGatedOpacity.d.ts.map +1 -1
  23. package/lib/MessagesContainer/components/DayAnimated/useScrollGatedOpacity.js +1 -1
  24. package/lib/MessagesContainer/components/DayAnimated/useScrollGatedOpacity.js.map +1 -1
  25. package/lib/MessagesContainer/components/Item/index.d.ts.map +1 -1
  26. package/lib/MessagesContainer/components/Item/index.js +13 -11
  27. package/lib/MessagesContainer/components/Item/index.js.map +1 -1
  28. package/lib/MessagesContainer/index.d.ts.map +1 -1
  29. package/lib/MessagesContainer/index.js +71 -42
  30. package/lib/MessagesContainer/index.js.map +1 -1
  31. package/lib/MessagesContainer/types.d.ts +1 -0
  32. package/lib/MessagesContainer/types.d.ts.map +1 -1
  33. package/lib/TypingIndicator/index.d.ts.map +1 -1
  34. package/lib/TypingIndicator/index.js +11 -5
  35. package/lib/TypingIndicator/index.js.map +1 -1
  36. package/lib/components/TouchableOpacity.d.ts.map +1 -1
  37. package/lib/components/TouchableOpacity.js +10 -3
  38. package/lib/components/TouchableOpacity.js.map +1 -1
  39. package/lib/hooks/useUpdateLayoutEffect.d.ts.map +1 -1
  40. package/lib/hooks/useUpdateLayoutEffect.js +3 -0
  41. package/lib/hooks/useUpdateLayoutEffect.js.map +1 -1
  42. package/lib/linkParser.d.ts +14 -0
  43. package/lib/linkParser.d.ts.map +1 -1
  44. package/lib/linkParser.js +103 -34
  45. package/lib/linkParser.js.map +1 -1
  46. package/llms.txt +175 -0
  47. 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">
@@ -34,33 +34,63 @@
34
34
 
35
35
  ---
36
36
 
37
+ ## Quick start
38
+
39
+ ```bash
40
+ npx expo install @kesha-antonov/react-native-chat react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
41
+ ```
42
+
43
+ ```jsx
44
+ import { useCallback, useState } from 'react'
45
+ import { Chat } from '@kesha-antonov/react-native-chat'
46
+
47
+ export function Example () {
48
+ const [messages, setMessages] = useState([
49
+ {
50
+ _id: 1,
51
+ text: 'Hello developer',
52
+ createdAt: new Date(),
53
+ user: { _id: 2, name: 'John Doe', avatar: 'https://placeimg.com/140/140/any' },
54
+ },
55
+ ])
56
+
57
+ const onSend = useCallback((newMessages = []) => {
58
+ setMessages(previous => Chat.append(previous, newMessages))
59
+ }, [])
60
+
61
+ return <Chat messages={messages} onSend={onSend} user={{ _id: 1 }} />
62
+ }
63
+ ```
64
+
65
+ That is the whole setup - everything below is optional.
66
+
67
+ ---
68
+
37
69
  ## ✨ Features
38
70
 
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
71
+ - 🌗 **[Dark mode & theming](#theming--dark-mode)** - full light/dark token system, switchable at runtime
72
+ - 🤖 **[Streaming AI messages](#streaming-ai-messages)** - token-by-token replies with a typing caret and stop control
73
+ - 😀 **[Emoji reactions](#emoji-reactions)** - long-press picker and reaction pills
74
+ - 🎥 **[Video & audio messages](#-whats-new-vs-react-native-gifted-chat)** - real inline playback, with a tappable fallback
75
+ - 🎙️ **[Voice & video recording](#-whats-new-vs-react-native-gifted-chat)** - Telegram-style hold-to-record notes (opt-in)
76
+ - 📍 **[Location messages](#-whats-new-vs-react-native-gifted-chat)** - map card that opens the system maps app
77
+ - ↩️ **[Swipe to reply](#reply-to-messages)** - reply preview and message threading
78
+ - 📋 **[Long-press actions](#message-actions-long-press-context-menu)** - themed context menu anchored to the bubble
79
+ - ⚡ **[Quick replies](#quick-replies)** - bot-style reply buttons
80
+ - 🔗 **[Smart link parsing](#smart-link-parsing)** - URLs, emails, phones, hashtags, mentions
81
+ - 👤 **[Avatars](#avatars)** - user initials or custom images
82
+ - ✍️ **[Typing indicator](#typing-indicator)** - show when someone is typing
83
+ - ⏮️ **[Load earlier messages](#load-earlier-messages)** - pagination and infinite scroll
84
+ - ⬇️ **[Scroll to bottom](#scroll-to-bottom)** - quick jump back to the latest
85
+ - ✅ **[Message status](#message-status)** - ticks for sent, delivered and read
86
+ - 💬 **[System messages](#system-messages)** - in-line notifications
87
+ - 🌍 **[Localized dates & i18n](#localization-i18n)** - Day.js dates, translated UI strings
88
+ - ⌨️ **[Keyboard handling](#keyboard--layout)** - smart avoidance on every platform
89
+ - 🎨 **[Fully customizable](#-props-reference)** - override any component with your own
90
+ - 📱 **[Expo](#-installation)** & 🌐 **[Web](#web-react-native-web)** - works with Expo and react-native-web
91
+ - 📝 **[TypeScript](#typescript)** - complete definitions, generic over your message type
92
+
93
+ Actively maintained, New Architecture ready, built for the latest Reanimated / Gesture Handler.
64
94
 
65
95
  <p align="center">
66
96
  <img width="200" src="https://github.com/user-attachments/assets/c9da88f5-0b20-471c-8cd7-373bdb767517" />
@@ -74,9 +104,10 @@
74
104
 
75
105
  ## 🆕 What's new vs react-native-gifted-chat
76
106
 
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)).
107
+ 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
108
 
79
- On top of that, this fork adds the features people kept asking upstream for, plus a modern look out of the box:
109
+ <details>
110
+ <summary><strong>Side-by-side comparison</strong></summary>
80
111
 
81
112
  | Added in this fork | react-native-gifted-chat | @kesha-antonov/react-native-chat |
82
113
  | --- | :---: | :---: |
@@ -92,10 +123,13 @@ On top of that, this fork adds the features people kept asking upstream for, plu
92
123
  | **Location messages** | ❌ ignored | ✅ map card → opens system maps |
93
124
  | Bubble tails + tighter message grouping | flat bubbles | ✅ |
94
125
 
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.
126
+ </details>
96
127
 
97
128
  ### Theming in one line
98
129
 
130
+ <details>
131
+ <summary><strong>Override any token, light and dark</strong></summary>
132
+
99
133
  ```jsx
100
134
  // Modern defaults out of the box, or override any token (light + dark):
101
135
  <Chat
@@ -105,8 +139,13 @@ Everything new is **non-breaking and opt-in**: keep passing the same props you d
105
139
  />
106
140
  ```
107
141
 
142
+ </details>
143
+
108
144
  ### Voice, video and location
109
145
 
146
+ <details>
147
+ <summary><strong>Opt-in recording and media playback</strong></summary>
148
+
110
149
  ```jsx
111
150
  <Chat
112
151
  audioRecording={{ isEnabled: true }} // hold the mic to record a voice note (needs expo-audio)
@@ -121,9 +160,14 @@ Everything new is **non-breaking and opt-in**: keep passing the same props you d
121
160
  npx expo install expo-video expo-audio expo-image-picker
122
161
  ```
123
162
 
163
+ </details>
164
+
124
165
  ### Custom icons (e.g. Lucide)
125
166
 
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:
167
+ <details>
168
+ <summary><strong>Override any built-in icon</strong></summary>
169
+
170
+ 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
171
 
128
172
  ```tsx
129
173
  import { Send, Mic } from 'lucide-react-native'
@@ -139,6 +183,8 @@ import { Send, Mic } from 'lucide-react-native'
139
183
 
140
184
  Overridable names: `send`, `mic`, `camera`, `play`, `pause`, `check`, `checkAll`, `clock`, `pin`, `plus`, `close`, `chevronLeft`, `chevronDown`, `emoji`, `paperclip`, `reply`, `pencil`, `lock`, `trash`.
141
185
 
186
+ </details>
187
+
142
188
  ---
143
189
 
144
190
  <h3 align="center">Support This Project</h3>
@@ -153,18 +199,35 @@ Overridable names: `send`, `mic`, `camera`, `play`, `pause`, `check`, `checkAll`
153
199
 
154
200
  ---
155
201
 
202
+ > **Using an AI coding agent?** [`llms.txt`](llms.txt) is a condensed integration guide for
203
+ > agents adding this library to an app, and [`AGENTS.md`](AGENTS.md) covers working inside
204
+ > this repository.
205
+
156
206
  ## 📖 Table of Contents
157
207
 
208
+ - [Quick start](#quick-start)
158
209
  - [Features](#-features)
159
210
  - [What's new vs react-native-gifted-chat](#-whats-new-vs-react-native-gifted-chat)
160
211
  - [Requirements](#-requirements)
161
212
  - [Installation](#-installation)
162
213
  - [Migrating from react-native-gifted-chat](#-migrating-from-react-native-gifted-chat)
163
214
  - [Usage](#-usage)
164
- - [Props Reference](#-props-reference)
165
215
  - [Data Structure](#-data-structure)
216
+ - [Props Reference](#-props-reference)
217
+ - [Guides](#-guides)
218
+ - [Reply to Messages](#reply-to-messages)
219
+ - [Maintaining Scroll Position (AI Chatbots)](#maintaining-scroll-position-ai-chatbots)
220
+ - [Streaming (AI) Messages](#streaming-ai-messages)
221
+ - [Emoji Reactions](#emoji-reactions)
222
+ - [Smart Link Parsing](#smart-link-parsing)
223
+ - [Message actions](#message-actions-long-press-context-menu)
224
+ - [Theming & Dark Mode](#theming--dark-mode)
225
+ - [Localization (i18n)](#localization-i18n)
226
+ - [Message Status](#message-status)
227
+ - [TypeScript](#typescript)
166
228
  - [Platform Notes](#-platform-notes)
167
229
  - [Performance](#-performance)
230
+ - [Testing](#-testing)
168
231
  - [Example App](#-example-app)
169
232
  - [Troubleshooting](#-troubleshooting)
170
233
  - [Contributing](#-contributing)
@@ -175,13 +238,9 @@ Overridable names: `send`, `mic`, `camera`, `play`, `pause`, `check`, `checkAll`
175
238
 
176
239
  ## 📋 Requirements
177
240
 
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) |
241
+ | React Native | iOS | Android | Expo | TypeScript |
242
+ |---|---|---|---|---|
243
+ | >= 0.70.0 | >= 13.4 | API 21+ (5.0) | SDK 50+ | >= 5.0 (optional) |
185
244
 
186
245
  ---
187
246
 
@@ -195,33 +254,37 @@ npx expo install @kesha-antonov/react-native-chat react-native-reanimated react-
195
254
 
196
255
  ### Bare React Native Projects
197
256
 
198
- **Step 1:** Install the packages
257
+ <details>
258
+ <summary><strong>Three steps</strong></summary>
259
+
260
+ **1.** Install the packages:
199
261
 
200
- Using yarn:
201
262
  ```bash
202
263
  yarn add @kesha-antonov/react-native-chat react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
203
264
  ```
204
265
 
205
- Using npm:
206
266
  ```bash
207
267
  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
268
  ```
209
269
 
210
- **Step 2:** Install iOS pods
270
+ **2.** Install iOS pods:
211
271
 
212
272
  ```bash
213
273
  npx pod-install
214
274
  ```
215
275
 
216
- **Step 3:** Configure react-native-reanimated
276
+ **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
277
 
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.
278
+ </details>
219
279
 
220
280
  ---
221
281
 
222
282
  ## 🔄 Migrating from react-native-gifted-chat
223
283
 
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.
284
+ **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)**.
285
+
286
+ <details>
287
+ <summary><strong>What to rename</strong></summary>
225
288
 
226
289
  ```bash
227
290
  yarn remove react-native-gifted-chat
@@ -236,7 +299,7 @@ yarn add @kesha-antonov/react-native-chat
236
299
  | `GiftedChatContext` | `ChatContext` |
237
300
  | `IMessage`, `User`, `useChatContext`, … | unchanged |
238
301
 
239
- See the full guide (codemod included) in **[docs/MIGRATION.md](docs/MIGRATION.md)**.
302
+ </details>
240
303
 
241
304
  ---
242
305
 
@@ -244,47 +307,7 @@ See the full guide (codemod included) in **[docs/MIGRATION.md](docs/MIGRATION.md
244
307
 
245
308
  ### Basic Example
246
309
 
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.
310
+ 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
311
 
289
312
  ---
290
313
 
@@ -293,7 +316,7 @@ export function Example() {
293
316
  Messages, system messages, and quick replies follow the structure defined in [Models.ts](src/Models.ts).
294
317
 
295
318
  <details>
296
- <summary><strong>Message Object Structure</strong></summary>
319
+ <summary><strong>Message object structure</strong></summary>
297
320
 
298
321
  ```typescript
299
322
  interface IMessage {
@@ -345,6 +368,11 @@ interface User {
345
368
 
346
369
  ## 📖 Props Reference
347
370
 
371
+ Every group below is collapsed - open the one you need.
372
+
373
+ <details>
374
+ <summary><strong>Core configuration, refs &amp; theming</strong></summary>
375
+
348
376
  ### Core Configuration
349
377
 
350
378
  - **`messages`** _(Array)_ - Messages to display
@@ -363,13 +391,18 @@ interface User {
363
391
  - **`messagesContainerRef`** _(FlatList ref)_ - Ref to the flatlist
364
392
  - **`textInputRef`** _(TextInput ref)_ - Ref to the text input
365
393
 
394
+ </details>
395
+
396
+ <details>
397
+ <summary><strong>Keyboard &amp; layout</strong></summary>
398
+
366
399
  ### Keyboard & Layout
367
400
 
368
401
  - **`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
402
 
370
403
  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)).
404
+ - **`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.
405
+ - **`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
406
  - **`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
407
  - **`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
408
  - **`isInverted`** _(Bool)_ - Reverses display order of `messages`; default is `true`
@@ -390,6 +423,11 @@ If you do, sanity-check it on device: a toolbar behind the keyboard means the va
390
423
 
391
424
  > **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
425
 
426
+ </details>
427
+
428
+ <details>
429
+ <summary><strong>Text input, composer &amp; actions</strong></summary>
430
+
393
431
  ### Text Input & Composer
394
432
 
395
433
  - **`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 +454,11 @@ If you do, sanity-check it on device: a toolbar behind the keyboard means the va
416
454
  - **`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
455
  - **`actionSheetOptionTintColor`** _(String)_ - Tint color for action labels in the attachment sheet
418
456
 
457
+ </details>
458
+
459
+ <details>
460
+ <summary><strong>Messages, container &amp; bubbles</strong></summary>
461
+
419
462
  ### Messages & Message Container
420
463
 
421
464
  - **`messagesContainerStyle`** _(Object)_ - Custom style for the messages container
@@ -443,20 +486,16 @@ If you do, sanity-check it on device: a toolbar behind the keyboard means the va
443
486
  - **`imageProps`** _(Object)_ - Extra props to be passed to the [`<Image>`](https://reactnative.dev/docs/image) component created by the default `renderMessageImage`
444
487
  - **`imageStyle`** _(Object)_ - Custom style for message images
445
488
  - **`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:
489
+ - **`messageTextProps`** _(Object)_ - Extra props to be passed to the MessageText component. Useful for customizing link parsing behavior, text styles, and matchers:
447
490
  - `matchers` - Custom matchers for linking message content (like URLs, phone numbers, hashtags, mentions)
448
491
  - `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')
492
+ - `email` / `phone` / `url` - Enable/disable parsing (default: true)
493
+ - `hashtag` / `mention` - Enable/disable parsing (default: false)
494
+ - `hashtagUrl` / `mentionUrl` - Base URLs (e.g. `'https://x.com/hashtag'`)
456
495
  - `stripPrefix` - Strip 'http://' or 'https://' from URL display (default: false)
457
496
  - `TextComponent` - Custom Text component to use (e.g., from react-native-gesture-handler)
458
497
 
459
- Example:
498
+ A custom matcher, replacing the default phone linking with an action sheet:
460
499
 
461
500
  ```tsx
462
501
  <Chat
@@ -504,7 +543,12 @@ Example:
504
543
  />
505
544
  ```
506
545
 
507
- See full example in [LinksExample](example/components/chat-examples/LinksExample.tsx)
546
+ See the full example in [LinksExample](example/components/chat-examples/LinksExample.tsx).
547
+
548
+ </details>
549
+
550
+ <details>
551
+ <summary><strong>Avatars, username, date &amp; time, system messages</strong></summary>
508
552
 
509
553
  ### Avatars
510
554
 
@@ -546,17 +590,20 @@ See full example in [LinksExample](example/components/chat-examples/LinksExample
546
590
 
547
591
  - **`renderSystemMessage`** _(Component | Function)_ - Custom system message
548
592
 
593
+ </details>
594
+
595
+ <details>
596
+ <summary><strong>Load earlier, typing indicator, quick replies, scroll to bottom</strong></summary>
597
+
549
598
  ### Load Earlier Messages
550
599
 
551
- - **`loadEarlierMessagesProps`** _(Object)_ - Props to pass to the LoadEarlierMessages component. The button is only visible when `isAvailable` is `true`. Supports the following props:
600
+ - **`loadEarlierMessagesProps`** _(Object)_ - Props to pass to the LoadEarlierMessages component. The button is only visible when `isAvailable` is `true`:
552
601
  - `isAvailable` - Controls button visibility (default: false)
553
602
  - `onPress` - Callback when button is pressed
554
603
  - `isLoading` - Display loading indicator (default: false)
555
604
  - `isInfiniteScrollEnabled` - Enable infinite scroll up when reaching the top of messages container, automatically calls `onPress` (not yet supported for web)
556
605
  - `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
606
+ - `containerStyle` / `wrapperStyle` / `textStyle` - Custom styles for the button
560
607
  - `activityIndicatorStyle` - Custom style for the loading indicator
561
608
  - `activityIndicatorColor` - Color of the loading indicator (default: 'white')
562
609
  - `activityIndicatorSize` - Size of the loading indicator (default: 'small')
@@ -564,14 +611,14 @@ See full example in [LinksExample](example/components/chat-examples/LinksExample
564
611
 
565
612
  ### Typing Indicator
566
613
 
567
- - **`isTyping`** _(Bool)_ - Typing Indicator state; default `false`. If you use`renderFooter` it will override this.
614
+ - **`isTyping`** _(Bool)_ - Typing Indicator state; default `false`. If you use `renderFooter` it will override this.
568
615
  - **`renderTypingIndicator`** _(Component | Function)_ - Custom typing indicator component
569
616
  - **`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.
617
+ - **`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
618
 
572
619
  ### Quick Replies
573
620
 
574
- See [Quick Replies example in messages.ts](example/example-expo/data/messages.ts)
621
+ See the [quick replies example in messages.ts](example/example-expo/data/messages.ts).
575
622
 
576
623
  - **`onQuickReply`** _(Function)_ - Callback when sending a quick reply (to backend server)
577
624
  - **`renderQuickReplies`** _(Function)_ - Custom all quick reply view
@@ -580,11 +627,26 @@ See [Quick Replies example in messages.ts](example/example-expo/data/messages.ts
580
627
  - **`quickReplyContainerStyle`** _(StyleProp<ViewStyle>)_ - Custom container style for quick replies
581
628
  - **`renderQuickReplySend`** _(Function)_ - Custom quick reply **send** view
582
629
 
630
+ ### Scroll to Bottom
631
+
632
+ - **`isScrollToBottomEnabled`** _(Bool)_ - Enables the scroll to bottom Component (Default is false)
633
+ - **`scrollToBottomComponent`** _(Function)_ - Custom Scroll To Bottom Component container
634
+ - **`scrollToBottomOffset`** _(Integer)_ - Custom Height Offset upon which to begin showing Scroll To Bottom Component (Default is 200)
635
+ - **`scrollToBottomStyle`** _(Object)_ - Custom style for Scroll To Bottom wrapper (position, bottom, right, etc.)
636
+ - **`scrollToBottomContentStyle`** _(Object)_ - Custom style for Scroll To Bottom content (size, background, shadow, etc.)
637
+
638
+ </details>
639
+
640
+ ---
641
+
642
+ ## 🧩 Guides
643
+
583
644
  ### Reply to Messages
584
645
 
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.
646
+ <details>
647
+ <summary><strong>Swipe-to-reply, reply preview and threading</strong></summary>
586
648
 
587
- > **Note:** This feature uses `ReanimatedSwipeable` from `react-native-gesture-handler` and `react-native-reanimated` for smooth, performant animations.
649
+ 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
650
 
589
651
  #### Basic Usage
590
652
 
@@ -604,8 +666,6 @@ React Native Chat supports swipe-to-reply functionality out of the box. When ena
604
666
 
605
667
  #### Reply Props (Grouped)
606
668
 
607
- The `reply` prop accepts an object with the following structure:
608
-
609
669
  ```typescript
610
670
  interface ReplyProps<TMessage> {
611
671
  // Swipe gesture configuration
@@ -652,8 +712,6 @@ interface ReplyProps<TMessage> {
652
712
 
653
713
  #### ReplyMessage Structure
654
714
 
655
- When a message has a reply, it includes a `replyMessage` property:
656
-
657
715
  ```typescript
658
716
  interface ReplyMessage {
659
717
  _id: string | number
@@ -695,23 +753,15 @@ const [replyMessage, setReplyMessage] = useState<ReplyMessage | null>(null)
695
753
 
696
754
  #### Smooth Animations
697
755
 
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.
704
-
705
- ### Scroll to Bottom
756
+ 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.
706
757
 
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.)
758
+ </details>
712
759
 
713
760
  ### Maintaining Scroll Position (AI Chatbots)
714
761
 
762
+ <details>
763
+ <summary><strong>Keep the reading position while long answers stream in</strong></summary>
764
+
715
765
  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
766
 
717
767
  ```tsx
@@ -749,15 +799,20 @@ const [isScrolledUp, setIsScrolledUp] = useState(false)
749
799
  />
750
800
  ```
751
801
 
802
+ </details>
803
+
752
804
  ### Streaming (AI) Messages
753
805
 
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.
806
+ 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
807
 
756
808
  <p align="center">
757
809
  <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
810
  </p>
759
811
 
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.
812
+ <details>
813
+ <summary><strong>useStreamingMessages, stop control and a full example</strong></summary>
814
+
815
+ 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
816
 
762
817
  - **`IMessage.streaming`** - flag a message as streaming (shows the caret)
763
818
  - **`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 +841,13 @@ function Bot () {
786
841
 
787
842
  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
843
 
844
+ </details>
845
+
789
846
  #### Markdown rendering for AI replies
790
847
 
848
+ <details>
849
+ <summary><strong>Built-in renderer, or react-native-streamdown</strong></summary>
850
+
791
851
  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
852
 
793
853
  ```tsx
@@ -813,9 +873,11 @@ There are two renderers and you get the best available one automatically:
813
873
  <Chat messageTextProps={{ markdown: true, markdownProps: { /* ... */ } }} {...props} />
814
874
  ```
815
875
 
876
+ </details>
877
+
816
878
  ### Emoji Reactions
817
879
 
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.
880
+ 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
881
 
820
882
  <p align="center">
821
883
  <img width="200" src="https://raw.githubusercontent.com/kesha-antonov/react-native-chat/main/media/reactions-picker.png" />
@@ -825,6 +887,9 @@ Long-press a message to open a quick emoji picker; selected reactions render as
825
887
  <img width="200" src="https://raw.githubusercontent.com/kesha-antonov/react-native-chat/main/media/reactions-emoji-browser.png" />
826
888
  </p>
827
889
 
890
+ <details>
891
+ <summary><strong>Wiring up the toggle, and the full prop list</strong></summary>
892
+
828
893
  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
894
 
830
895
  ```tsx
@@ -874,8 +939,6 @@ const handleReactionPress = useCallback((message: IChatMessage, emoji: string) =
874
939
 
875
940
  #### Reactions Props (Grouped)
876
941
 
877
- The `reactions` prop accepts:
878
-
879
942
  - **`isEnabled`** _(Bool)_ - Enable emoji reactions (default `false`)
880
943
  - **`emojis`** _(String[])_ - Emojis shown in the quick picker (default `['👍', '❤️', '😂', '😮', '😢', '👎']`)
881
944
  - **`onReactionPress`** _(Function)_ - `(message, emoji) => void` called when an emoji is selected or a pill is tapped. Toggle logic is left to you
@@ -884,8 +947,13 @@ The `reactions` prop accepts:
884
947
  - **`containerStyle`**, **`reactionStyle`**, **`reactionActiveStyle`**, **`reactionTextStyle`**, **`reactionCountStyle`** - Styles for the reaction pills
885
948
  - **`pickerContainerStyle`**, **`pickerEmojiStyle`** - Styles for the quick picker
886
949
 
950
+ </details>
951
+
887
952
  ### Smart Link Parsing
888
953
 
954
+ <details>
955
+ <summary><strong>URLs, emails, phones, hashtags and mentions</strong></summary>
956
+
889
957
  Message text is automatically scanned for URLs, emails, and phone numbers; hashtags and mentions are opt-in. Configure it via `messageTextProps`:
890
958
 
891
959
  ```tsx
@@ -909,9 +977,14 @@ Message text is automatically scanned for URLs, emails, and phone numbers; hasht
909
977
 
910
978
  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
979
 
980
+ </details>
981
+
912
982
  ### Message actions (long-press context menu)
913
983
 
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.
984
+ <details>
985
+ <summary><strong>Telegram-style floating menu</strong></summary>
986
+
987
+ 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
988
 
916
989
  ```tsx
917
990
  import { setStringAsync } from 'expo-clipboard'
@@ -925,8 +998,15 @@ import { Copy, Trash2 } from 'lucide-react-native' // optional icons
925
998
  />
926
999
  ```
927
1000
 
1001
+ > **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.
1002
+
1003
+ </details>
1004
+
928
1005
  ### Interactive content inside bubbles (video players, maps)
929
1006
 
1007
+ <details>
1008
+ <summary><strong>Keeping native controls tappable</strong></summary>
1009
+
930
1010
  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
1011
 
932
1012
  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 +1020,13 @@ If a message must own every touch that lands on it, set `isMessageGestureEnabled
940
1020
  />
941
1021
  ```
942
1022
 
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.
1023
+ </details>
944
1024
 
945
1025
  ### Theming & Dark Mode
946
1026
 
1027
+ <details>
1028
+ <summary><strong>Token groups, runtime switching and themed components</strong></summary>
1029
+
947
1030
  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
1031
 
949
1032
  ```tsx
@@ -975,9 +1058,14 @@ const MyBadge = () => {
975
1058
 
976
1059
  Also exported: `defaultLightTheme`, `defaultDarkTheme`, and the `ChatTheme` / `PartialChatTheme` types.
977
1060
 
1061
+ </details>
1062
+
978
1063
  ### Localization (i18n)
979
1064
 
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`:
1065
+ <details>
1066
+ <summary><strong>Built-in translations and label overrides</strong></summary>
1067
+
1068
+ 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
1069
 
982
1070
  ```tsx
983
1071
  <Chat
@@ -989,8 +1077,13 @@ All built-in UI strings (composer placeholder, send/cancel, load earlier, today,
989
1077
 
990
1078
  Exported helpers: `ChatLabels` (type), `defaultLabels`, `translations`, `resolveLabels`, and the `useLabels` hook for reading the resolved labels in custom components.
991
1079
 
1080
+ </details>
1081
+
992
1082
  ### Message Status
993
1083
 
1084
+ <details>
1085
+ <summary><strong>Tick indicators for sent / delivered / read</strong></summary>
1086
+
994
1087
  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
1088
 
996
1089
  ```tsx
@@ -1013,8 +1106,13 @@ Customize the indicators with `renderTicks` (full override) or `tickStyle` (styl
1013
1106
  />
1014
1107
  ```
1015
1108
 
1109
+ </details>
1110
+
1016
1111
  ### TypeScript
1017
1112
 
1113
+ <details>
1114
+ <summary><strong>Generic over your own message type</strong></summary>
1115
+
1018
1116
  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
1117
 
1020
1118
  ```tsx
@@ -1031,8 +1129,7 @@ interface MyMessage extends IMessage {
1031
1129
  />
1032
1130
  ```
1033
1131
 
1034
- ---
1035
-
1132
+ </details>
1036
1133
 
1037
1134
  ---
1038
1135
 
@@ -1068,43 +1165,68 @@ For **Expo**, you can append `KeyboardAvoidingView` after Chat (Android only):
1068
1165
 
1069
1166
  ### Web (react-native-web)
1070
1167
 
1168
+ Install the web renderer alongside the library:
1169
+
1170
+ ```bash
1171
+ yarn add react-native-web react-dom
1172
+ ```
1173
+
1071
1174
  <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
- }
1175
+ <summary><strong>Expo / Metro (recommended) - no extra config</strong></summary>
1176
+
1177
+ ```bash
1178
+ npx expo start --web
1179
+ ```
1180
+
1181
+ 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.
1182
+
1183
+ </details>
1184
+
1185
+ <details>
1186
+ <summary><strong>Other bundlers (webpack, Vite, Next.js) - the package must be transpiled</strong></summary>
1187
+
1188
+ 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:
1189
+
1190
+ 1. Alias `react-native` to `react-native-web` and let the resolver pick up `.web.js` / `.web.tsx` extensions
1191
+ 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`
1192
+
1193
+ 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.
1194
+
1195
+ </details>
1196
+
1197
+ ### React Strict DOM
1198
+
1199
+ <details>
1200
+ <summary><strong>Works inside an RSD tree - verified on iOS and Expo web</strong></summary>
1201
+
1202
+ 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:
1203
+
1204
+ ```tsx
1205
+ import { css, html } from 'react-strict-dom'
1206
+ import { Chat } from '@kesha-antonov/react-native-chat'
1207
+
1208
+ const styles = css.create({
1209
+ root: { display: 'flex', flexDirection: 'column', height: '100%' },
1210
+ })
1211
+
1212
+ <html.div data-layoutconformance='strict' style={styles.root}>
1213
+ <Chat messages={messages} onSend={onSend} user={user} />
1214
+ </html.div>
1097
1215
  ```
1098
1216
 
1217
+ Three things to know:
1218
+
1219
+ - **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
1220
+ - **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
1221
+ - **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
1222
+
1099
1223
  </details>
1100
1224
 
1101
1225
  ---
1102
1226
 
1103
1227
  ## ⚡ Performance
1104
1228
 
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:
1229
+ 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
1230
 
1109
1231
  ```jsx
1110
1232
  // ❌ New reference every render - the row can't skip
@@ -1113,12 +1235,14 @@ The chat is built for long lists, but a few habits on your side unlock the most:
1113
1235
  // ✅ Stable references - unchanged rows skip re-renders
1114
1236
  const renderBubble = useCallback(props => <MyBubble {...props} />, [])
1115
1237
  const reactions = useMemo(() => ({ isEnabled: true, onReactionPress }), [onReactionPress])
1116
- const messageActions = useCallback(message => [{ label: 'Copy', onPress: () => copy(message.text) }], [copy])
1117
1238
 
1118
- <Chat renderBubble={renderBubble} reactions={reactions} messageActions={messageActions} />
1239
+ <Chat renderBubble={renderBubble} reactions={reactions} />
1119
1240
  ```
1120
1241
 
1121
- This applies to all render props (`renderBubble`, `renderMessageText`, `renderAvatar`, ...), the `reactions` / `audioRecording` / `videoRecording` / `messageActions` config objects, and any style objects.
1242
+ <details>
1243
+ <summary><strong>Immutability, context props and virtualization tuning</strong></summary>
1244
+
1245
+ The memoization advice applies to all render props (`renderBubble`, `renderMessageText`, `renderAvatar`, ...), the `reactions` / `audioRecording` / `videoRecording` / `messageActions` config objects, and any style objects.
1122
1246
 
1123
1247
  **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
1248
 
@@ -1130,8 +1254,13 @@ This applies to all render props (`renderBubble`, `renderMessageText`, `renderAv
1130
1254
  <Chat listProps={{ windowSize: 7, removeClippedSubviews: true }} {...props} />
1131
1255
  ```
1132
1256
 
1257
+ </details>
1258
+
1133
1259
  ### FlashList (opt-in)
1134
1260
 
1261
+ <details>
1262
+ <summary><strong>Recycling rows on very long histories</strong></summary>
1263
+
1135
1264
  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
1265
 
1137
1266
  ```bash
@@ -1165,6 +1294,8 @@ Notes:
1165
1294
  - `FlatList`-only knobs (`windowSize`, `maxToRenderPerBatch`, `initialNumToRender`, `updateCellsBatchingPeriod`, `removeClippedSubviews`) are not forwarded to FlashList - it sizes its own render window.
1166
1295
  - If `@shopify/flash-list` is not installed, the prop is ignored, a warning is logged, and `FlatList` is used.
1167
1296
 
1297
+ </details>
1298
+
1168
1299
  ---
1169
1300
 
1170
1301
  ## 🧪 Testing
@@ -1197,33 +1328,22 @@ fireEvent(loadingWrapper, 'layout', {
1197
1328
 
1198
1329
  ## 📦 Example App
1199
1330
 
1200
- The repository includes a comprehensive example app demonstrating all features:
1331
+ <details>
1332
+ <summary><strong>Running the demo locally</strong></summary>
1333
+
1334
+ 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
1335
 
1202
1336
  ```bash
1203
- # Clone and install
1204
1337
  git clone https://github.com/kesha-antonov/react-native-chat.git
1205
1338
  cd react-native-chat/example
1206
1339
  yarn install
1207
1340
 
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
1341
+ npx expo run:ios # iOS
1342
+ npx expo run:android # Android
1343
+ npx expo start --web # Web
1216
1344
  ```
1217
1345
 
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
1346
+ </details>
1227
1347
 
1228
1348
  ---
1229
1349
 
@@ -1277,37 +1397,24 @@ See [this issue](https://github.com/kesha-antonov/react-native-chat/issues/298)
1277
1397
 
1278
1398
  ## 🤝 Contributing
1279
1399
 
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
1400
+ Contributions are welcome - fork, branch, and open a Pull Request.
1292
1401
 
1293
1402
  ### Development Setup
1294
1403
 
1295
- ```bash
1296
- # Install dependencies
1297
- yarn install
1298
-
1299
- # Build the library
1300
- yarn build
1404
+ <details>
1405
+ <summary><strong>Commands</strong></summary>
1301
1406
 
1302
- # Run tests
1303
- yarn test
1407
+ ```bash
1408
+ yarn install # install dependencies
1409
+ yarn build # build the library
1410
+ yarn test # run tests
1411
+ yarn lint # run linting
1412
+ yarn prepublishOnly # full validation
1413
+ ```
1304
1414
 
1305
- # Run linting
1306
- yarn lint
1415
+ Before opening a PR, please make sure `yarn test`, `yarn lint` and `yarn build` all pass.
1307
1416
 
1308
- # Full validation
1309
- yarn prepublishOnly
1310
- ```
1417
+ </details>
1311
1418
 
1312
1419
  ---
1313
1420