In this article, we will walk you through how to quickly build a React Native chat app using Twilio-based back-end along with Gifted Chat for styling.
Why Businesses Must Have Chat Functionality in Their Applications
Nowadays, when the competition for customer’s attention is higher than ever, speed can become a determinative factor while choosing a company. Whether it comes to overnight shipping or anytime 24/7 access to the interested service or product, your customers, in most cases, don’t want to wait for long — and they usually get things as quickly as possible. It follows that the customer service speed has a big impact on their potential to convert.
Research shows that the average response time for a customer service request on social media is about 10 hours and up to 12 hours on email responses. As a consequence, such long delays can cost your customers. The solution is live chat apps.
Live chat is software that enables you to have a real-time conversation with potential customers within one platform. There are many options out there for live chat software, each with different functions and features, which you can integrate into your website and in web and mobile applications. Among the commonly used features are authorization, messaging, group chatting, notifications, and file sharing. Businesses can also add some extra features like video calls or voice messages in order to provide clients with extensive functionality.
Live chat software is convenient because users don’t need to switch from platform to platform, and it is quick due to the real-time response. Therefore, businesses, both small and large, use live chat software to serve their customers better and improve response time.
Along with providing quick customer support, the usage of chat software can positively impact your business in some other ways. So what are the additional benefits of implementing live chat functionality for businesses? Let’s have a look.
Building Trust with Customers
Live chat software allows you to have direct interaction with your customers. That enables your business to build trust and increase customer loyalty by answering questions and providing online support with any difficulties during the interaction with the website and your company.
A study done on global consumer trends by Oracle shows that a live chat button gives 90% of consumers confidence that they will get help when and if needed. Without a live chat option, you will need to work much harder on building trust with consumers that visit your website.
Understanding Customer Needs Better
Live chat software lets you see your product from a customer’s perspective. It can be helpful for product enhancements, marketing and sales purposes, or the way you grow your business.
You can collect data through live chat about your customers’ behavior, their questions, and issues they have with your website and work on them to eliminate fears and doubts concerning your products and services.
According to the Aberdeen Group report, companies that systematically listen to their customers and use this information to take action enjoy 55% greater customer retention rates and an almost 10-fold increase in the company’s annual revenue from the previous year than all other companies.
Satisfying Customers More Fully
Implementation of live chat on your website or app gives you an excellent opportunity to improve your customer service and provide a memorable customer experience while interacting with your brand. That is why customers generally prefer using live chat instead of email or phone support.
Live chat has the highest satisfaction levels for any customer service channel, with 73%, compared with 61% for email and 44% for phone. The reason for such high customer satisfaction is the efficiency and immediacy of the experience.
Communicating with customers via a channel they prefer the most will help you to improve their experience, and in return, retain more consumers.
Improving Website Experience
Live chat creates a comfortable environment for customers when they don’t need to call you or email you when a question comes up. Instead, with live chat, they can have their questions answered immediately by a human. It can be especially important when a customer needs an initial consultation before purchasing or subscribing to a product or service online.
Along with helping clients by improving the website experience, businesses get more purchases. Research shows that customers who use live chat on a website are three times more likely to make a purchase, which will positively impact your bottom line.
All types of businesses benefit from using live chat software. For marketplaces, live chats will have the biggest impact. Such real-time interactions through live chat connect your marketplace’s buyers and sellers and help in driving engagement and sales.
Live chat can not only conveniently connect your buyers and sellers and build trust across your marketplace but help drive more opportunities to convert by successfully closing the loop. For example, you may use notifications to keep the conversation moving so users stay connected and never miss a chance to transact.
Live chat gives you a chance to go through the purchase funnel together with a prospect customer, help them overcome objections, and reach a buying decision. It’s like having a sales assistant on standby, live on your website, and it can be crucial for your marketplace.
Why to Choose React Native For Chat App Development
Despite being a new and young framework, React Native already holds the trust of leading titans of the industry, including Facebook, Instagram, Walmart, Bloomberg, Tesla, Vogue, UberEats, Skype, and more. Among all these, Facebook Messenger is globally recognized as the top rank messaging app that React Native supports. The reason being, this framework makes your app effective in presenting the combined benefits of hybrid and native apps together under one roof.
Here are the top reasons why they have considered React Native for their company and why it is becoming a soaring framework for char software projects.
Easy Availability of Talent
According to Stack Overflow’s Developer Survey, about 58% of respondents are already familiar with React Native and express the interest in continuing to develop with it. Secondly, being at the top position and used by 42% of global developers, React Native is already becoming popular among tech companies, therefore, finding the best RN developer in the market is not an exigent job.
Lower Development Cost
Because you can write codes that run on both platforms, the development cost to hire app developers is dramatically reduced if compared to developing native solutions. Even if you don’t require a team of engineers to develop a solution, a single React Native app developer can help you create the app for both platforms.
Reusable UI Components
When building a React Native app, the first thing that developers consider is to start defining the components that appear in many places within the app. These components are like reusable blocks, so written once and can be used in many places without spending more and more time on development and hence, save cost and time on an app development project.
Compatibility to Cross-Platform
For cross-platform support, we are talking about iOS and Android. Initially, when created by a team of Facebook, it came only with iOS support in 2015, and started to support Android later in the same year. Since then, the popularity of this framework has taken the world by storm and is constantly growing. With this framework at your service, all you need is to hire an app developer with expertise in UI elements and APIs. A developer can create an application that will seamlessly run on iOS, Android and the web by simply using a single codebase.
That is why if you want to build basic chat functionality into your application — whether it is a marketplace, e-learning platform, or telemedicine portal — you must bet on React Native first and foremost.
Building Chat Functionality into Your App with React Native
In this study, we will walk you through the process of building a simple chat app with React Native and Expo using Twilio and Sendbird as the backend services. Our piece of software will contain the following features and functions:
- ‘Signup/Login’ feature — allows a user to access the application.
- ‘Channels List’ feature — allows a user to handle the list of messaging channels to chat in.
- ‘New Chat’ feature — allows a user to create a new chat or join an existing one.
- ‘Chat’ feature — allows a user to input characters, send and receive messages in real-time.
Here’s what we will cover in this article:
- How to start a new chat app project with Expo;
- How to set up a backend for a chat app;
- How to build a few chat features with React Native.
Step 1: Starting a New Project with Expo
Expo is a framework that is used for building React Native apps. It is basically a bundle with tools and services developed for React Native, which will help you make React Native apps with ease.
To start a new project, you must use an Expo CLI, a command-line app that is the main interface between a developer and Expo tools. To begin with the app, you must have to install Expo CLI on your local machine. For a hassle-free installation, a mobile app developer can run the below command in the terminal in order to install the CLI and generate a new project using it.
Installing Expo CLI: npm install — global expo-cli
Creating a project ChatApp: expo init ChatApp
Navigating to the project directory: cd ChatApp
Starting a development server: expo start
After generating a project, it’s time to run it on our development machine to ensure that everything works smoothly. Expo allows developers to open the project on multiple devices simultaneously or use an iOS simulator or Android emulator.
The next step is to install a dependency called react-native-gifted-chat that offers us a customizable UI for a chat application and ensures seamless Navigation between screens. For navigation between multiple screens, we use React-Navigation, and at last, we can connect with the back-end chat API service (for our project, we used Twilio and Sendbird). Here is the example of a quick short command:
npm install express dotenv twilio axios twilio-chat sendbird redux react-redux @react-navigation/native react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view @react-navigation/stack react-native-gifted-chat
Step 2: Choosing a Back-end for a Chat App
When you create a chat app, you need to understand that the application will need to process and store a lot of data, including text messages, videos, images, and files of different types. Perhaps, the most important consideration when creating a chat app is to choose the right platform for server-side implementation. Your back end must be reliable, scalable, and support millions of connections per node.
There are plenty of such platforms available in the market. We prefer using off-the-shelf messaging SDK solutions because it’s the best way to reduce time-consuming efforts, increase performance and focus on the core content. For our project, we decided to opt for Twilio and Sendbird, which we believe are among the best messaging SDKs for React Native applications. In this chapter, we will focus on adding chat features into your app using Twilio Programmable Chat. The creation of chat app functionality via Sendbird SDK will be considered in detail in the second part of the article.
Twilio Programmable Chat makes it easy for you to add chat features into your web and mobile apps without building or scaling a real-time chat backend. Chat has all of the necessary APIs and features to integrate with your business logic to ensure you’re in control.
Step 3: Setting up a Twilio-Powered Server
Before getting started, we need to generate an access token to authorize the React Native app to communicate with the Programmable Twilio Chat.
To set up our backend for Chat, we need four values from our Twilio account. They will be stored in our .env
file:
- Service instance SID — a service instance where all the data for our application is stored and scoped;
- Account SID — a primary Twilio account identifier;
- API key — used to authenticate;
- API secret — used to authenticate.
To take your Twilio account SID go to the Twilio Console and then copy and paste it as the value TWILIO_ACCOUNT_SID
to the .env
file.
Next, we need to generate an API key and API secret. After creating them, copy and paste the SID and the secret as the values TWILIO_API_KEY
and TWILIO_API_SECRET
.
Furtherly, we need to create a new Chat Service by navigating to All Products & Services > Programmable Chat > Services > Chat Services.
Let’s copy and paste the service SID as the value TWILIO_CHAT_SERVICE_SID
.
Finally, our .env
file should look like this:
TWILIO_ACCOUNT_SID=your_account_sid
TWILIO_API_KEY=your_api_key
TWILIO_API_SECRET=your_api_secret
TWILIO_CHAT_SERVICE_SID=your_chat_service_sid
When our .env
is ready, we can create a simple server with a single GET request from — /token/:identity. This route will request and return a token from TWILIO. Let’s install dependencies for our server: yarn add express dotenv twilio
Then we can begin configuring our Twilio server and service. Here is the configuration of our Twilio-based server that will receive and process the requests:
require('dotenv').config();
const Twilio = require('twilio');
const express = require('express');
const nodemailer = require('nodemailer');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(bodyParser.raw());
const AccessToken = Twilio.jwt.AccessToken;
const ChatGrant = AccessToken.ChatGrant;
// async..await is not allowed in global scope, must use a wrapper
async function sendMail(userEmail) {
// Generate test SMTP service account from ethereal.email
// Only needed if you don't have a real mail account for testing
// let testAccount = await nodemailer.createTestAccount();
// TO SEND EMAILS FROM GMAIL ACCOUNT ENABLE LESS SECURE APPS
// https://www.google.com/settings/security/lesssecureapps
const email = '***********@upsilonit.com';
const password = '***********';
const receiver = ''***********@upsilonit.com';
// create reusable transporter object using the default SMTP transport
let transporter = nodemailer.createTransport({
name: 'gmail.com',
host: 'gmail.com',
port: 587,
// secure: false, // true for 465, false for other ports
// auth: {
// user: testAccount.user, // generated ethereal user
// pass: testAccount.pass, // generated ethereal password
// },
service: 'Gmail',
auth: {
user: email,
pass: password,
},
tls: {
rejectUnauthorized: false,
},
});
// send mail with defined transport object
let info = await transporter.sendMail({
from: `"Chat App" <${email}>`, // sender address
to: receiver, // list of receivers
subject: `Someone with email ${userEmail} logged in your ChatApp`, // Subject line
text: `Someone with email ${userEmail} logged in your ChatApp`, // plain text body
html: `<b>Someone with email ${userEmail} logged in your ChatApp</b>`, // html body
});
console.log('Message sent: %s', info);
// Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@example.com>
// Preview only available when sending through an Ethereal account
console.log('Preview URL: %s', nodemailer.getTestMessageUrl(info));
// Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou...
}
app.get('/token/:identity', (req, res) => {
const identity = req.params.identity;
const token = new AccessToken(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_API_KEY,
process.env.TWILIO_API_SECRET
);
token.identity = identity;
token.addGrant(
new ChatGrant({
serviceSid: process.env.TWILIO_CHAT_SERVICE_SID,
})
);
res.send({
identity: token.identity,
jwt: token.toJwt(),
});
});
app.post('/sendMail', function (req, res) {
console.log(req.body);
sendMail(req.body.userEmail);
res.send('POST request to the sendMail page');
});
app.listen(3001, function () {
console.log('Programmable Chat server running on port 3001!');
});
And here is the configuration of service that will be used for getting users’ data, their tokens, channels, chats list, and their messages:
import { Client } from 'twilio-chat';
import axios from 'axios';
export const getToken = (username) =>
axios
.get(`http://localhost:3001/token/${username}`)
.then((twilioUser) => twilioUser.data.jwt);
export class TwilioService {
static serviceInstance;
static chatClient;
constructor() {}
static getInstance() {
if (!TwilioService.serviceInstance) {
TwilioService.serviceInstance = new TwilioService();
}
return TwilioService.serviceInstance;
}
async getChatClient(twilioToken) {
if (!TwilioService.chatClient && !twilioToken) {
throw new Error('Twilio token is null or undefined');
}
if (!TwilioService.chatClient && twilioToken) {
return Client.create(twilioToken).then((client) => {
TwilioService.chatClient = client;
return TwilioService.chatClient;
});
}
return Promise.resolve().then(() => TwilioService.chatClient);
}
clientShutdown() {
TwilioService.chatClient?.shutdown();
TwilioService.chatClient = null;
}
addTokenListener(getToken) {
if (!TwilioService.chatClient) {
throw new Error('Twilio client is null or undefined');
}
TwilioService.chatClient.on('tokenAboutToExpire', () => {
getToken().then(TwilioService.chatClient.updateToken);
});
TwilioService.chatClient.on('tokenExpired', () => {
getToken().then(TwilioService.chatClient.updateToken);
});
return TwilioService.chatClient;
}
parseChannels(channels) {
return channels.map(this.parseChannel);
}
parseChannel(channel) {
return {
id: channel.sid,
name: channel.friendlyName,
createdAt: channel.dateCreated,
updatedAt: channel.dateUpdated,
lastMessageTime:
channel.lastMessage?.dateCreated ??
channel.dateUpdated ??
channel.dateCreated,
};
}
parseMessages(messages) {
return messages.map(this.parseMessage).reverse();
}
parseMessage(message) {
return {
_id: message.sid,
text: message.body,
createdAt: message.dateCreated,
user: {
_id: message.author,
name: message.author,
},
received: true,
};
}
}
At last, we can run the server with the following command:
node server.js
Step 4: Developing Chat App Functionality with Twilio
When our Twilio-powered server is set up, we can start building a sample chat with basic functionality.
‘Signup/Login’ feature development
Let’s start with the signup/login feature that will allow a user to access the application.
Like any other messenger, our chat needs to authenticate a user firstly. In Twilio, all chat SDK clients need a valid Access Token to be able to authenticate and interact with the chat service. As we authenticate a user, the Token is generated on the server and then is utilized by the Chat SDK client to authorize with the Chat Service. Have a look at how it’s done in our sample chat app:
import axios from 'axios';
import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import { Input, Button } from 'react-native-elements';
import { LoginTwilioProps } from '../../navigation/types';
const LoginTwilio = ({ navigation }: LoginTwilioProps) => {
const [username, setUsername] = useState('');
const [error, setError] = useState('');
const [emailOK, setEmailOK] = useState(true);
const sendEmail = async () => {
const response = await axios({
method: 'post',
url: 'http://localhost:3001/sendMail/',
data: {
userEmail: username,
},
headers: {
'Content-Type': 'application/json',
},
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
};
const onButtonPress = () => {
if (validateEmail(username)) {
setEmailOK(true);
setError('');
sendEmail();
navigation.navigate('ChatLists', { username });
} else {
setEmailOK(false);
setError('Enter correct email');
}
};
const validateEmail = (email: string) => {
const regex =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regex.test(String(email).toLowerCase());
};
return (
<View style={styles.container}>
<View style={styles.box}>
<Input
label="Email"
value={username}
style={emailOK ? {} : styles.error}
labelStyle={styles.input}
errorMessage={error}
onChangeText={setUsername}
autoCapitalize="none"
/>
</View>
<View style={styles.box}>
<Button
buttonStyle={styles.button}
title="Connect"
onPress={onButtonPress}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
},
box: {
marginTop: 10,
},
error: { borderBottomColor: 'red', borderBottomWidth: 3 },
input: { color: '#000' },
button: { backgroundColor: '#2096f3' },
});
export default LoginTwilio;
Channels List’ feature development
This feature allows a user to handle the list of messaging channels to chat in. On the ‘Channels List’ screen, we output a list of channels joined by a user and the time of the last message received. While this screen loads up, we are loading all existing channel data using the Token we generated during the user authentication. The channels are sorted by the time the last message was added. Also, Twilio allows implementing many options of sorting the channels: for example, display one-to-one chats at the top and group channels after them. For more information, read the documentation.
So, we made the ‘Channel List’ feature with the following code:
import React, { useState } from 'react';
import { View, ActivityIndicator, StyleSheet } from 'react-native';
import { Button, Input } from 'react-native-elements';
import { TwilioService } from '../twilio-service';
export function CreateChatScreen() {
const [channelName, setChannelName] = useState('');
const [loading, setLoading] = useState(false);
const onCreateOrJoin = async () => {
setLoading(true);
try {
const client = await TwilioService.getInstance().getChatClient();
try {
const channel = await client.getChannelByUniqueName(channelName);
if (channel.channelState.status !== 'joined') {
await channel.join().then(() => alert('You have joined.'));
} else {
alert('You have already joined the channel.');
}
} catch (error) {
const newChannel = await client.createChannel({
uniqueName: channelName,
friendlyName: channelName,
});
await newChannel.join().then(() => alert('You have joined.'));
} finally {
setLoading(false);
}
} catch (error) {
alert(error);
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<Input
label="Channel Name"
value={channelName}
labelStyle={styles.inputStyle}
onChangeText={setChannelName}
/>
<Button title="Create Or Join" onPress={onCreateOrJoin} />
{loading && (
<ActivityIndicator style={styles.loader} color="#000" size="large" />
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#fff',
paddingHorizontal: 20,
},
inputStyle: { color: '#000' },
loader: { marginTop: 20 },
});
‘New Chat’ feature development
This feature allows a user to create a new chat or join an existing one. On this screen, the user must enter the name of a new chat in the special field and then click on the ‘Create or Join’ button. On the server, we must decide, based on the token request sent to us, who the user is and if he has access to this chat. Depending on this, the user is either added to the existing chat or creates a new one and is automatically added there. If the user has joined the chat before, the app informs the user about that via a service message. That’s how it works in our sample chat app:
import React, { useState } from 'react';
import { View, ActivityIndicator, StyleSheet } from 'react-native';
import { Button, Input } from 'react-native-elements';
import { TwilioService } from '../twilio-service';
export function CreateChatScreen() {
const [channelName, setChannelName] = useState('');
const [loading, setLoading] = useState(false);
const onCreateOrJoin = async () => {
setLoading(true);
try {
const client = await TwilioService.getInstance().getChatClient();
try {
const channel = await client.getChannelByUniqueName(channelName);
if (channel.channelState.status !== 'joined') {
await channel.join().then(() => alert('You have joined.'));
} else {
alert('You have already joined the channel.');
}
} catch (error) {
const newChannel = await client.createChannel({
uniqueName: channelName,
friendlyName: channelName,
});
await newChannel.join().then(() => alert('You have joined.'));
} finally {
setLoading(false);
}
} catch (error) {
alert(error);
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<Input
label="Channel Name"
value={channelName}
labelStyle={styles.inputStyle}
onChangeText={setChannelName}
/>
<Button title="Create Or Join" onPress={onCreateOrJoin} />
{loading && (
<ActivityIndicator style={styles.loader} color="#000" size="large" />
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#fff',
paddingHorizontal: 20,
},
inputStyle: { color: '#000' },
loader: { marginTop: 20 },
});
‘Chat’ feature development
This feature allows a user to send and receive messages in real-time and displays those messages inside a chat room. For this feature development, we used an open-source library called react-native-gifted-chat.
Upon the ‘Chat’ screen initialization, we subscribe to the ‘events.’ They are functions working during the period the screen is displayed and track the changes in the channel (for example, when a new message is sent. By subscribing to the ‘events,’ we can fetch the messages sent in that channel and listen for updates for when a new message is sent. Also, at this step, we get the channel ID, load up all its messages and send them to the react-native-gifted-chat for displaying. That’s how it works in our sample chat app:
import React, { useState, useCallback, useEffect, useRef } from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import { GiftedChat } from 'react-native-gifted-chat';
import { TwilioChatScreenRouteProp } from '../../navigation/types';
import { TwilioService } from '../twilio-service';
const ChatRoomScreen = ({ route }: TwilioChatScreenRouteProp) => {
const { channelId, identity } = route.params;
const [messages, setMessages] = useState([]);
const [loading, setLoading] = useState(true);
const chatClientChannel = useRef();
const chatMessagesPaginator = useRef();
const setChannelEvents = useCallback((channel) => {
chatClientChannel.current = channel;
chatClientChannel.current.on('messageAdded', (message) => {
const newMessage = TwilioService.getInstance().parseMessage(message);
const { giftedId } = message.attributes;
if (giftedId) {
setMessages((prevMessages) => {
if (prevMessages.some(({ _id }) => _id === giftedId)) {
return prevMessages.map((m) =>
m._id === giftedId ? newMessage : m
);
}
return [newMessage, ...prevMessages];
});
}
});
return chatClientChannel.current;
}, []);
const initTwilioChannel = useCallback(async () => {
setLoading(true);
try {
const client = await TwilioService.getInstance().getChatClient();
const channel = await client.getChannelBySid(channelId);
const currentChannel = await setChannelEvents(channel);
const paginator = await currentChannel.getMessages();
chatMessagesPaginator.current = paginator;
const newMessages = await TwilioService.getInstance().parseMessages(
paginator.items
);
setMessages(newMessages);
} catch (error) {
alert(error.message);
} finally {
setLoading(false);
}
}, [channelId, setChannelEvents]);
useEffect(() => {
initTwilioChannel();
}, []);
const onSend = useCallback((newMessages = []) => {
const attributes = { giftedId: newMessages[0]._id };
setMessages((prevMessages) => GiftedChat.append(prevMessages, newMessages));
chatClientChannel.current?.sendMessage(newMessages[0].text, attributes);
}, []);
return (
<View style={styles.screen}>
{loading ? (
<ActivityIndicator style={styles.loader} color="#000" size="large" />
) : (
<GiftedChat
messagesContainerStyle={styles.messageContainer}
messages={messages}
renderAvatarOnTop
renderUsernameOnMessage
onSend={(messages) => onSend(messages)}
user={{ _id: identity }}
/>
)}
</View>
);
};
const styles = StyleSheet.create({
screen: {
flexGrow: 1,
backgroundColor: '#FFF',
},
messageContainer: {
backgroundColor: '#f3f3f3',
},
loader: { marginTop: 20 },
});
export default ChatRoomScreen;
Here is the first part of the article 'How to Build Chat Functionality into Your App with React Native'. In it, we talked about the main benefits chat apps can give your business, why React Native is the best choice for chat app development, what Expo is, and showed our experience in building chat apps with Twilio SDK. In the second part, we will look at the development process using Sendbird SDK and make a comparison of these two platforms.
The second part is about to come. Subscribe to our newsletter in order not to miss the second part and other helpful content!
Top comments (1)
Thanks a lot for the comprehensive tutorial! For even more advanced features, I usually use this React Native Chat, to save months of hard work