Skip to main content

What's new vs Gifted Chat

Kept in sync with upstream 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.

Side-by-side comparison
Added in this forkreact-native-gifted-chat@kesha-antonov/react-native-chat
Active maintenance💤 sporadic✅ active
Modern default UI (modern messenger look)dated 2020 look✅ wallpaper, tails, inline time, fully overridable
Light/Dark theme systemper-component color props✅ theme / darkTheme tokens, runtime switch
Streaming (AI) messages❌✅ token-by-token + typing cursor
Emoji reactions❌✅ long-press picker + reaction pills
Emoji keyboard❌✅ built in: tabs, search, recents, skin tones
Attachment menu❌ (actions sheet)✅ built in: photo grid + caption, files, location
File messages❌✅ IMessage.file document bubble
Swipe-to-reply + reply preview❌✅ built in
Video / audio messages"not implemented, render your own"✅ inline players + tappable fallback
Voice notes (hold-to-record + waveform)❌✅ optional
Video messages (round camera notes)❌✅ optional
Location messages❌ ignored✅ map card → opens system maps
Bubble tails, inline time + run groupingflat bubbles✅

Theming in one line​

Override any token, light and dark
// Modern defaults out of the box, or override any token (light + dark):
<Chat
theme={{ colors: { accent: '#3390EC', outgoingBubble: '#EFFEDE' } }}
darkTheme={{ colors: { background: '#0E1621' } }}
{...props}
/>

Voice, video and location​

Recording and media playback

Voice notes (expo-audio) and round video notes (react-native-vision-camera) turn on by themselves once their peers are installed: the mic button records on hold, and a tap switches it to the camera. Pass false to turn either off.

<Chat
videoRecording={{ maxDuration: 30 }} // optional: configure; `false` turns round notes off
mediaPlayback={{ playbackRate: 1.5 }} // optional: start notes at 1.5x, auto-play the next note
// location messages render automatically for any IMessage with a `location`
{...props}
/>
# Optional inline media playback + recording:
npx expo install expo-video expo-audio expo-image-picker
# Optional: round video notes and the voice-note waveform
# (vision-camera 5+, which runs on Nitro and needs its two Nitro packages too)
npx expo install react-native-vision-camera react-native-nitro-modules react-native-nitro-image react-native-audio-api react-native-svg expo-haptics
# Optional: trim paused voice takes, blur behind the round-video camera
npx expo install expo-file-system expo-blur

Recording and playback work like this:

  • Hold the record button to record, release to send. With both voice and video enabled, a tap switches it between mic and camera, and the choice is remembered. With only video, the camera sits in the record button's place.
  • Slide left to cancel: all the way across cancels at once; releasing past about half the way cancels too. Slide up to lock and keep recording hands-free. The two exclude each other: no lock once the cancel slide is under way. "Slide up to lock" is shown for the first few takes.
  • Locked, the bar shows a m:ss,t timer and a CANCEL button, and the lock turns into a pause button.
  • A paused voice take turns into a preview: its waveform with play, to send or delete. The mic above send resumes recording onto its end, and the handles at either end of the waveform trim it. Resuming needs react-native-audio-api; trimming also needs expo-file-system, and a trimmed take is sent as a WAV.
  • A paused round video take holds the recording open: the circle shows the frame it paused on (Android; iOS keeps the live camera, dimmed), the bar offers delete and send, and the camera above send resumes into the same video. Round takes can't be trimmed.
  • A round note records on the first hold, with the camera drawn over a blurred chat (when expo-blur is installed). Switch camera and flash work while recording (the screen lights up for the front camera, the torch for the back one), and you can pinch to zoom. At its 60s limit the take stops into a looping preview (tap to hear it) instead of sending.
  • Haptic ticks on start, stop, lock and mode switch when expo-haptics is installed.
  • Starting a recording pauses any note that is playing.
  • Only one note plays at a time. When a voice or round video note plays to its end, the next newer one in the chat starts.
  • While a note plays or is paused, a "now playing" bar at the top of the chat has play/pause, the sender, the speed (1x / 1.5x / 2x; long-press for more, shared by every note) and close; a tap on the title scrolls to the note.
  • Round notes rest in the list on their first frame with a play button (or loop silently with isRoundVideoAutoplayEnabled). A tap plays one from the start with sound and grows it, hiding the sender's avatar; a tap pauses it, and a paused note can be scrubbed by dragging around its ring. Its length, in a pill at its bottom-left, counts down while it plays, and playing bars replace its unread dot once it is heard.
  • Set isMediaUnread on a message for the unread dot; mediaPlayback.onMediaRead reports when an incoming one is played.

Emoji keyboard and attachment menu​

Built in, on by default

The composer's emoji button opens the built-in emoji keyboard in the system keyboard's place - at the last keyboard height, so switching between the two keeps the composer still. It has the category tabs, search (scroll the grid up to reveal it), recents by use count, a skin-tone popup on long-press and a repeating backspace. A tap inserts at the caret whether or not you control text.

The paperclip opens the built-in attachment menu: your newest photos in a 3-column grid (with expo-media-library), numbered as you pick them, a caption and a "Send N" button; File and Location tabs; a camera tile. Picks arrive in onSend as ordinary messages - image, video, file ({ uri, name, size?, mimeType? }) or location - so there is nothing to wire.

# Each tab shows only when its package is installed:
npx expo install expo-media-library expo-image-picker expo-document-picker expo-location
# Optional: keep emoji recents across launches, share received files, fast ph:// thumbnails
npx expo install @react-native-async-storage/async-storage expo-sharing expo-image
<Chat
emojiKeyboard={{ columns: 8, recents: { storageKey: 'my-app/emoji' } }}
attachments={{
tabs: ['gallery', 'file'],
// Upload first, then send what your backend returns (or `null` to send nothing).
onPick: async (picked, { caption }) => upload(picked, caption),
}}
{...props}
/>

Hide either with emojiKeyboard={false} / attachments={false}. onPressEmoji replaces the emoji keyboard and actions / onPressActionButton / renderActions replace the attachment menu, as before.

Permissions. iOS kills an app that asks for a permission without a usage string. In Expo, list the plugins with your own strings (the defaults are generic):

"plugins": [
["expo-image-picker", { "photosPermission": "...", "cameraPermission": "..." }],
["expo-media-library", { "photosPermission": "...", "savePhotosPermission": "...", "granularPermissions": ["photo", "video"] }],
["expo-location", { "locationWhenInUsePermission": "..." }],
"expo-document-picker",
"expo-sharing"
]

In a bare app add the keys yourself, then rebuild the native app:

PackageiOS Info.plistAndroid manifest
expo-media-libraryNSPhotoLibraryUsageDescription (NSPhotoLibraryAddUsageDescription to save)READ_MEDIA_IMAGES, READ_MEDIA_VIDEO (READ_EXTERNAL_STORAGE below Android 13)
expo-image-pickerNSPhotoLibraryUsageDescription, NSCameraUsageDescription (NSMicrophoneUsageDescription for video)CAMERA (and RECORD_AUDIO for video)
expo-locationNSLocationWhenInUseUsageDescriptionACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION
expo-document-pickernone (iCloud documents need the iCloud entitlement)none
expo-sharingnonenone

Custom icons (e.g. Lucide)​

Override any built-in icon

Built-in icons are the official Lucide glyphs, rendered via the optional react-native-svg peer when it is installed, or drawn with Views (no dependency) otherwise. Override any of them via the icons prop - the built-in icon is used for anything you don't override:

import { Send, Mic } from 'lucide-react-native'

<Chat
icons={{
send: ({ color, size }) => <Send color={color} size={size} />,
mic: ({ color, size }) => <Mic color={color} size={size} />,
}}
{...props}
/>

Overridable names: send, mic, camera, play, pause, check, checkAll, clock, pin, plus, close, chevronLeft, chevronDown, emoji, paperclip, reply, pencil, lock, trash.


Support This Project

I maintain this project in my free time with no compensation. If you find it useful and want to help keep it alive, please consider sponsoring. Your support means a lot! 💖

Become a Sponsor


Using an AI coding agent? llms.txt is a condensed integration guide for agents adding this library to an app, and AGENTS.md covers working inside this repository.