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