@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.
- package/README.md +318 -211
- package/lib/Bubble/index.d.ts.map +1 -1
- package/lib/Bubble/index.js +1 -0
- package/lib/Bubble/index.js.map +1 -1
- package/lib/Chat/index.d.ts.map +1 -1
- package/lib/Chat/index.js +36 -15
- package/lib/Chat/index.js.map +1 -1
- package/lib/Chat/types.d.ts +12 -11
- package/lib/Chat/types.d.ts.map +1 -1
- package/lib/InputToolbar.d.ts.map +1 -1
- package/lib/InputToolbar.js +3 -0
- package/lib/InputToolbar.js.map +1 -1
- package/lib/MessageImage.js +2 -2
- package/lib/MessageImage.js.map +1 -1
- package/lib/MessagesContainer/components/DayAnimated/debug.d.ts +1 -1
- package/lib/MessagesContainer/components/DayAnimated/debug.d.ts.map +1 -1
- package/lib/MessagesContainer/components/DayAnimated/debug.js +2 -2
- package/lib/MessagesContainer/components/DayAnimated/debug.js.map +1 -1
- package/lib/MessagesContainer/components/DayAnimated/index.d.ts.map +1 -1
- package/lib/MessagesContainer/components/DayAnimated/index.js +5 -5
- package/lib/MessagesContainer/components/DayAnimated/index.js.map +1 -1
- package/lib/MessagesContainer/components/DayAnimated/useScrollGatedOpacity.d.ts.map +1 -1
- package/lib/MessagesContainer/components/DayAnimated/useScrollGatedOpacity.js +1 -1
- package/lib/MessagesContainer/components/DayAnimated/useScrollGatedOpacity.js.map +1 -1
- package/lib/MessagesContainer/components/Item/index.d.ts.map +1 -1
- package/lib/MessagesContainer/components/Item/index.js +13 -11
- package/lib/MessagesContainer/components/Item/index.js.map +1 -1
- package/lib/MessagesContainer/index.d.ts.map +1 -1
- package/lib/MessagesContainer/index.js +71 -42
- package/lib/MessagesContainer/index.js.map +1 -1
- package/lib/MessagesContainer/types.d.ts +1 -0
- package/lib/MessagesContainer/types.d.ts.map +1 -1
- package/lib/TypingIndicator/index.d.ts.map +1 -1
- package/lib/TypingIndicator/index.js +11 -5
- package/lib/TypingIndicator/index.js.map +1 -1
- package/lib/components/TouchableOpacity.d.ts.map +1 -1
- package/lib/components/TouchableOpacity.js +10 -3
- package/lib/components/TouchableOpacity.js.map +1 -1
- package/lib/hooks/useUpdateLayoutEffect.d.ts.map +1 -1
- package/lib/hooks/useUpdateLayoutEffect.js +3 -0
- package/lib/hooks/useUpdateLayoutEffect.js.map +1 -1
- package/lib/linkParser.d.ts +14 -0
- package/lib/linkParser.d.ts.map +1 -1
- package/lib/linkParser.js +103 -34
- package/lib/linkParser.js.map +1 -1
- package/llms.txt +175 -0
- 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 & 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
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
|
179
|
-
|
|
180
|
-
|
|
|
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
|
-
|
|
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
|
-
**
|
|
270
|
+
**2.** Install iOS pods:
|
|
211
271
|
|
|
212
272
|
```bash
|
|
213
273
|
npx pod-install
|
|
214
274
|
```
|
|
215
275
|
|
|
216
|
-
**
|
|
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
|
-
|
|
278
|
+
</details>
|
|
219
279
|
|
|
220
280
|
---
|
|
221
281
|
|
|
222
282
|
## 🔄 Migrating from react-native-gifted-chat
|
|
223
283
|
|
|
224
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 & 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 & 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
|
-
- **`
|
|
372
|
-
- **`
|
|
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 & 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 & 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
|
|
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
|
|
450
|
-
- `
|
|
451
|
-
- `
|
|
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
|
-
|
|
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 & 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
|
|
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
|
|
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 [
|
|
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 [
|
|
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
|
-
|
|
646
|
+
<details>
|
|
647
|
+
<summary><strong>Swipe-to-reply, reply preview and threading</strong></summary>
|
|
586
648
|
|
|
587
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
>
|
|
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
|
|
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
|
-
|
|
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
|
-
>
|
|
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
|
-
|
|
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>
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
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
|
|
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}
|
|
1239
|
+
<Chat renderBubble={renderBubble} reactions={reactions} />
|
|
1119
1240
|
```
|
|
1120
1241
|
|
|
1121
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1209
|
-
npx expo run:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1296
|
-
|
|
1297
|
-
yarn install
|
|
1298
|
-
|
|
1299
|
-
# Build the library
|
|
1300
|
-
yarn build
|
|
1404
|
+
<details>
|
|
1405
|
+
<summary><strong>Commands</strong></summary>
|
|
1301
1406
|
|
|
1302
|
-
|
|
1303
|
-
yarn
|
|
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
|
-
|
|
1306
|
-
yarn lint
|
|
1415
|
+
Before opening a PR, please make sure `yarn test`, `yarn lint` and `yarn build` all pass.
|
|
1307
1416
|
|
|
1308
|
-
|
|
1309
|
-
yarn prepublishOnly
|
|
1310
|
-
```
|
|
1417
|
+
</details>
|
|
1311
1418
|
|
|
1312
1419
|
---
|
|
1313
1420
|
|