npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-soft-chat

v0.1.1

Published

A customizable React Native chat UI toolkit, built on MIT-licensed open-source chat components — theming, i18n/RTL, streaming AI messages, location cards, and full media playback, with an emphasis on type safety, performance, and customization

Readme

React Native Soft Chat

A customizable React Native chat UI toolkit based on MIT-licensed open-source chat components, with additional improvements and a modernized developer experience — one <SoftChat /> component with a fully typed API, render props for every visual piece, and the animated FlatList performance work (day labels, scroll-to-bottom, inverted lists) already done for you.

react-native-soft-chat is an independently developed package built on source originally from react-native-gifted-chat. It is not the official react-native-gifted-chat package, and it is not maintained by that project's authors — see License for the required attribution.

Installation

npm install react-native-soft-chat
yarn add react-native-soft-chat
pnpm add react-native-soft-chat

react-native-soft-chat also depends on a few native modules that most React Native projects already have, or that you'll need to install and configure per their own setup guides:

npm install react-native-gesture-handler react-native-reanimated react-native-keyboard-controller react-native-safe-area-context

Basic example

import React, { useCallback, useState } from 'react'
import { SoftChat, ChatMessage, ChatUser } from 'react-native-soft-chat'

const currentUser: ChatUser = { _id: 1, name: 'You' }

export default function ChatScreen () {
  const [messages, setMessages] = useState<ChatMessage[]>([])

  const onSend = useCallback((newMessages: ChatMessage[]) => {
    setMessages(previous => SoftChat.append(previous, newMessages))
  }, [])

  return (
    <SoftChat
      messages={messages}
      user={currentUser}
      onSend={onSend}
    />
  )
}

More scenarios — custom bubbles, custom input, avatars, theming, message types, pagination, empty states, message actions, and TypeScript usage — are in examples/.

Features

  • Typed data modelChatMessage and ChatUser are plain interfaces you can extend with your own fields (delivery receipts, attachments, anything) and keep full type safety through every render prop and callback.
  • Render props for everything — bubbles, avatars, the composer, send button, timestamps, system messages, day separators, and the input toolbar can each be swapped out independently.
  • A full design-token theme system — override colors, radii, spacing, and typography via theme / darkTheme, with runtime light/dark switching.
  • Streaming (AI) messages — flag a message streaming: true for a live typing caret and automatic markdown rendering, driven by the bundled useStreamingMessages hook.
  • Media messages — text, image, video, and audio messages with real inline playback (via the optional expo-video / expo-audio peers) and a tappable fallback card when they're absent, plus a pinch-to-zoom image viewer out of the box.
  • Location messages — any message with a location: { latitude, longitude } renders a map card that opens the system maps app on tap — no extra dependency required.
  • Quick replies — inline suggested-reply chips with radio or checkbox selection.
  • Swipe-to-reply, emoji reactions, and a long-press context menu — optional, each with their own render props for the preview, the picker, and the action list.
  • PaginationloadEarlierMessagesProps drives both a manual "load earlier" button and infinite scroll-up, with an optional @shopify/flash-list engine swap for very long histories.
  • Animated day separators, scroll-to-bottom, and typing indicator built on Reanimated, tuned to avoid re-rendering the message list on every scroll frame.
  • Internationalization & RTL — 15 bundled UI translations, Day.js date localization, and layout mirroring for right-to-left languages.
  • Light/dark theming via a colorScheme override, independent of the OS setting.
  • Keyboard handling via react-native-keyboard-controller, with an escape hatch (disableKeyboardProvider) for apps that already mount their own provider.

Customization

Every built-in piece is replaceable through a render* prop on <SoftChat />. Each render prop receives the same props the built-in component would have, so you can also just re-render the built-in component with a subset of props overridden (see examples/05-theme-customization.tsx).

| Area | Prop | Renders | | --- | --- | --- | | Messages | renderBubble | ChatBubble — the message container (text/image/video/audio/custom view) | | | renderMessageText / renderMessageImage / renderMessageVideo / renderMessageAudio | Individual message content | | | renderCustomView | Extra content inside a bubble (isCustomViewBottom controls placement) | | | renderSystemMessage | Centered system notices (message.system: true) | | | renderTime | The per-message timestamp | | | renderDay | The date separator between days | | Avatars | renderAvatar | Per-message avatar (pass null to disable entirely) | | Input | renderInputToolbar | The whole composer bar (component, element, or function) | | | renderComposer | Just the text field | | | renderActions | The leading action button (attachments, etc.) | | | renderSend | The send button | | | renderAccessory | An optional second row below the composer | | Quick replies | renderQuickReplies / renderQuickReplySend | Suggested-reply chips and their send affordance | | List | renderChatEmpty | Empty-state content | | | renderFooter / renderChatFooter | Typing indicator area / content below the list | | | renderLoadEarlier | The "load earlier messages" control | | Reactions | reactions.renderReactions / reactions.renderReactionPicker | Reaction pills and the long-press picker | | Reply | reply.renderPreview / reply.renderMessageReply | The input-toolbar reply preview and the inline quoted-message |

Styling that doesn't need custom markup is usually available as a style prop directly (imageStyle, timeTextStyle, quickReplyStyle, typingIndicatorStyle, …); anything not exposed as a direct prop can be reached by re-rendering the relevant built-in component (e.g. ChatBubble's wrapperStyle/textStyle) from inside a render* prop.

Theming

Every built-in component reads its colors, radii, spacing, and typography from a single design token object, resolved once per render and shared through context — so switching theme, darkTheme, or the system color scheme updates every component at once:

<SoftChat
  theme={{ colors: { accent: '#3390EC', outgoingBubble: '#EFFEDE' } }}
  darkTheme={{ colors: { background: '#0E1621' } }}
  {...props}
/>

theme applies in light mode, darkTheme in dark mode; each is deep-merged over the built-in defaults, so you only need to specify the tokens you want to change. Reach the resolved theme from your own components with useTheme() / useThemedStyles(factory). Icons (send, mic, check, …) can be overridden the same way via the icons prop — pass your own icon library and the built-in glyph is used for anything you don't override.

Streaming (AI) messages

Mark a message streaming: true to show a live typing caret, and pair it with the bundled useStreamingMessages hook to manage the state:

import { SoftChat, useStreamingMessages } from 'react-native-soft-chat'

const { messages, append, startStream } = useStreamingMessages()

const handleSend = useCallback(async (newMessages) => {
  append(newMessages)

  const stream = startStream({ user: assistant })
  for await (const chunk of fetchAssistantReply(stream.signal))
    stream.push(chunk)
  stream.done()
}, [append, startStream])

<SoftChat messages={messages} onSend={handleSend} user={currentUser} />

Streaming messages render as markdown automatically — headings, lists, code blocks, and links are handled by a built-in dependency-free renderer, or by the optional react-native-streamdown peer (better handling of incomplete, mid-stream markdown) when it's installed.

Internationalization & RTL

Pass a locale (e.g. 'es', 'pt-BR') to switch both the 15 bundled UI translations and the Day.js date formatting; override any string directly with labels. Right-to-left languages (Arabic, Hebrew, Persian, Urdu, …) are detected automatically from locale and mirror this SoftChat instance's own layout — pass forceRTL to override the detection. This never touches React Native's global I18nManager, which is an app-wide, native-reload-requiring switch.

TypeScript

Every component, prop bag, and callback is fully typed and exported:

import type {
  ChatMessage,
  ChatUser,
  SoftChatProps,
  ChatBubbleProps,
  ChatComposerProps,
  ChatAvatarProps,
  QuickReplyOption,
  MessageReaction,
  MessageMenuItem,
  ChatTheme,
  ChatIcons,
  ChatLabels,
} from 'react-native-soft-chat'

SoftChat is generic over your message type — extend ChatMessage to add your own fields and every render prop and callback narrows to your extended type automatically. See examples/10-typescript-usage.tsx.

Performance

  • Keep messages a new array reference only when it actually changes; SoftChat.append / SoftChat.prepend return a new array without mutating the one you pass in.
  • The message list is inverted (newest message at index 0) by default, which lets the FlatList render new messages without re-measuring everything above them — avoid setting isInverted={false} unless you have a specific reason to.
  • Prefer stamping stable, unique _ids (e.g. from your backend) over regenerating them on every render; unstable ids defeat the list's item recycling.
  • Day separators are computed off the JS thread with Reanimated; disable them (isDayAnimationEnabled={false}) only if you've profiled and found they matter for your case.
  • Memoize render* props that close over component state (as the examples do with useCallback) so the list doesn't re-render every message row on unrelated state changes.
  • For very long histories, install @shopify/flash-list and pass isFlashListEnabled to swap the list's rendering engine; the message list falls back to FlatList (with a dev warning) when the package isn't installed, so it's always safe to leave the prop set.

Accessibility

  • The composer and send button carry accessibilityRole and accessibilityLabel values out of the box; avatars are marked with accessibilityRole="image".
  • The "load earlier messages" control exposes accessibilityRole="button" and disables itself (rather than hiding) while loading, so screen reader users get consistent feedback.
  • Custom render* components are your responsibility to make accessible the same way you would any other React Native view — the props passed in (e.g. currentMessage) give you everything needed to build meaningful labels.

Compatibility with react-native-gifted-chat

This package deliberately mirrors react-native-gifted-chat's public API wherever the functionality is the same, so it should feel immediately familiar. It is not a drop-in replacement — the main component is SoftChat, not GiftedChat, and a handful of names differ for concrete reasons — but most <GiftedChat /> code ports over by changing only the import and the component name:

  • Every SoftChatProps field that has a GiftedChatProps equivalent uses the same name and signaturemessages, user, onSend, locale, isTyping, isInverted, isSendButtonAlwaysVisible, every render* prop (renderBubble, renderAvatar, renderComposer, renderInputToolbar, renderMessageImage, renderMessageVideo, renderMessageAudio, renderCustomView, renderTime, renderDay, renderSystemMessage, renderQuickReplies, renderSend, renderActions, renderAccessory, renderChatFooter, renderUsername, renderLoading, …), and every callback (onPressAvatar, onLongPressAvatar, onPressMessage, onLongPressMessage, onQuickReply, onPressActionButton, messageIdGenerator), keep their original name and parameters.
  • The message and user models matchChatMessage/ChatUser have the exact same fields as IMessage/User (_id, text, createdAt, user, image, video, audio, system, sent, received, pending, quickReplies, …), plus additive fields for this package's own features (streaming, duration, videoNote, reactions, replyMessage). IMessage, IChatMessage, User, Reply, and QuickReplies are exported as type aliases of ChatMessage/ChatUser/QuickReplyOption/QuickRepliesPayload, so existing type annotations written against the original package's types keep working unchanged.

Where this package intentionally differs:

| Difference | Reason | | --- | --- | | Main component is SoftChat, not GiftedChat | This package's identity — not a functional difference. | | Canonical types are ChatMessage/ChatUser, not IMessage/User | ChatMessage drops the I-prefix ("Hungarian notation") modern TypeScript style avoids; both are exported as aliases, so either name works. | | Built-in components are exported as ChatBubble, ChatAvatar, ChatComposer, ChatInputToolbar, ChatActions, ChatSystemMessage, MessageRow, MessageList, DateSeparator, MessageTimestamp, LoadEarlierButton, UserAvatar, not Bubble, Avatar, Composer, InputToolbar, Actions, SystemMessage, Message, MessagesContainer, Day, Time, LoadEarlierMessages, GiftedAvatar | Bare names like Avatar, Bubble, Day, Time, Send, Message are easy for a published package to collide with a consumer's own component of the same name. The render* prop names you actually pass to <SoftChat /> — which is the API surface most apps touch — are unaffected; this only matters if you import a built-in component directly (e.g. to wrap it in a custom renderBubble). | | Send has an icon-based default (with an optional label for the original text button) instead of always rendering text | A modern default look; label/textStyle are still supported for the original text-button appearance. |

If you're porting an existing <GiftedChat /> screen, the mechanical part of the migration is usually just: change the import to react-native-soft-chat, rename the component to SoftChat, and — if you imported any of the built-in sub-components directly — add the Chat/ other prefix noted above.

License

MIT. react-native-soft-chat is derived from react-native-gifted-chat (Copyright (c) 2019 Farid from Safi), which is also MIT licensed. See LICENSE for the full text and attribution.