{"id":19076139,"url":"https://github.com/gapur/react-native-twilio-chat","last_synced_at":"2025-10-26T20:45:59.640Z","repository":{"id":37497938,"uuid":"323406699","full_name":"Gapur/react-native-twilio-chat","owner":"Gapur","description":"💬 Build a Twilio-Powered Chat App Using React Native","archived":false,"fork":false,"pushed_at":"2021-01-31T16:41:11.000Z","size":5251,"stargazers_count":30,"open_issues_count":4,"forks_count":17,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-13T07:20:19.541Z","etag":null,"topics":["chat","react","react-native","react-native-gifted-chat","twilio","twilio-api","twilio-chat"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Gapur.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-12-21T17:40:13.000Z","updated_at":"2025-01-03T01:33:54.000Z","dependencies_parsed_at":"2022-08-08T22:45:15.815Z","dependency_job_id":null,"html_url":"https://github.com/Gapur/react-native-twilio-chat","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Gapur/react-native-twilio-chat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gapur%2Freact-native-twilio-chat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gapur%2Freact-native-twilio-chat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gapur%2Freact-native-twilio-chat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gapur%2Freact-native-twilio-chat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gapur","download_url":"https://codeload.github.com/Gapur/react-native-twilio-chat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gapur%2Freact-native-twilio-chat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273416254,"owners_count":25101806,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chat","react","react-native","react-native-gifted-chat","twilio","twilio-api","twilio-chat"],"created_at":"2024-11-09T01:57:17.807Z","updated_at":"2025-10-26T20:45:54.601Z","avatar_url":"https://github.com/Gapur.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/twilio-logo.png\" /\u003e\n  \u003cbr/\u003e\n  React Native Twilio Chat\n\u003c/h1\u003e\n\nBuild a Twilio-Powered Chat App Using React Native\n\nQuickly get started with a Twilio Programmable Chat\n\nTwilio 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 the necessary APIs and features to integrate with your business logic to ensure you are in control.\n\nI wanted to build a quick, full-featured chat feature for my React Native app. I managed to do it with [Twilio Programmable Chat](https://www.twilio.com/docs/chat).\n\nI searched the internet a lot to find the best way to use Twilio Programmable Chat with React Native. Unfortunately, I couldn’t find much. So I decided to write an article about it, hopefully saving others some time.\n\n## Setting up the Project\n\nInstall the repository:\n```sh\ngit clone https://github.com/Gapur/react-native-twilio-chat.git\n```\n\nAfter that, move it into the react-native-twilio-chat directory and run it from the terminal:\n```\ncd react-native-twilio-chat\nnpm run ios\n```\n\n## Creating Our Server\n\nBefore we get started, We need to generate an access token to authorize the React Native app to communicate with the Programmable Twilio Chat.\nTo set up our backend for Chat, we’ll need four values from our Twilio account. We’ll store these in our .env file:\n- Service instance SID—a service instance where all the data for our application is stored and scoped\n- Account SID — your primary Twilio account identifier\n- API key — used to authenticate\n- API secret — used to authenticate\n\nNow, if your account is ready, you can find your account SID on the [Twilio Console](https://www.twilio.com/console). You should copy and paste it as the value TWILIO_ACCOUNT_SID to the .env file.\n\n\u003cp\u003e\n  \u003cimg width=\"800\"src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/account_sid.png\"\u003e\n\u003c/p\u003e\n\nNext, We need to generate an API key and API secret by navigating to Settings \u003e API Keys \u003e New API Key.\n\n\u003cp\u003e\n  \u003cimg width=\"800\"src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/new_api_key.png\"\u003e\n\u003c/p\u003e\n\nIf you create these things successfully, let’s copy and paste the SID and secret as the values TWILIO_API_KEY and TWILIO_API_SECRET.\n\n\u003cp\u003e\n  \u003cimg width=\"800\"src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/sid_and_secret_key.png\"\u003e\n\u003c/p\u003e\n\nLast, we need to create a new Chat Service by navigating to All Products \u0026 Services \u003e Programmable Chat \u003e Services \u003e Chat Services.\n\n\u003cp\u003e\n  \u003cimg width=\"800\"src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/chat_service.png\"\u003e\n\u003c/p\u003e\n\nLet’s copy and paste the service SID as the value TWILIO_CHAT_SERVICE_SID.\n\nFinally, our .env file should look like this:\n```js\nTWILIO_ACCOUNT_SID=your_account_sid\nTWILIO_API_KEY=your_api_key\nTWILIO_API_SECRET=your_api_secret\nTWILIO_CHAT_SERVICE_SID=your_chat_service_sid\n```\n\nWhen our .env is ready, we can create a simple server with a single GET route, /token/:identity. This route will request and return a token from TWILIO. Let’s install dependencies for our server:\n\n```sh\nyarn add express dotenv twilio\n```\n\nCreate our server.js:\n```js\nrequire('dotenv').config();\n\nconst Twilio = require('twilio');\nconst express = require('express');\n\nconst app = express();\n\nconst AccessToken = Twilio.jwt.AccessToken;\nconst ChatGrant = AccessToken.ChatGrant;\n\napp.get('/token/:identity', (req, res) =\u003e {\n  const identity = req.params.identity;\n  const token = new AccessToken(\n    process.env.TWILIO_ACCOUNT_SID,\n    process.env.TWILIO_API_KEY,\n    process.env.TWILIO_API_SECRET,\n  );\n\n  token.identity = identity;\n  token.addGrant(\n    new ChatGrant({\n      serviceSid: process.env.TWILIO_CHAT_SERVICE_SID,\n    }),\n  );\n\n  res.send({\n    identity: token.identity,\n    jwt: token.toJwt(),\n  });\n});\n\napp.listen(3001, function () {\n  console.log('Programmable Chat server running on port 3001!');\n});\n```\n\nThat’s it for our server. Now, We can run our server with the following command line:\n```sh\nnode server.js\n```\n\n## React Native Navigation\n\nIn order to show you the Twilio Programmable Chat in action, I’m going to build a full-featured app on React Native. Our app will have four screens: WelcomeScreen, ChatListScreen, ChatRoomScreen, and ChatCreateScreen.\n\nWe need a router to navigate between screens in our React Native app. So I’m going to use the [react-native-navigation](https://github.com/wix/react-native-navigation) library. React Native Navigation provides 100% native-platform navigation on both iOS and Android. We should install it with the required packages:\n\n```sh\nyarn add @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\n```\n\n## Welcome Screen\n\nWe’ll start with the welcome screen. Let’s create welcome-screen.js and add the following code:\n\n```js\nexport function WelcomeScreen({ navigation }) {\n  const [username, setUsername] = useState('');\n\n  return (\n    \u003cView style={styles.screen}\u003e\n      \u003cImage style={styles.logo} source={images.logo} /\u003e\n      \u003cText style={styles.titleText}\u003eWelcome to Twilio Chat\u003c/Text\u003e\n      \u003cTextInput\n        value={username}\n        onChangeText={setUsername}\n        style={styles.input}\n        placeholder=\"Username\"\n        placeholderTextColor={colors.ghost}\n      /\u003e\n      \u003cTouchableOpacity\n        disabled={!username}\n        style={styles.button}\n        onPress={() =\u003e navigation.navigate(routes.ChatList.name, { username })}\u003e\n        \u003cText style={styles.buttonText}\u003eLogin\u003c/Text\u003e\n      \u003c/TouchableOpacity\u003e\n    \u003c/View\u003e\n  );\n}\n```\n\nWe’ll use the username to generate the Twilio access token.\n\n\u003cp\u003e\n  \u003cimg width=\"800\"src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/welcome-screen.png\"\u003e\n\u003c/p\u003e\n\n## Chat-Create Screen\n\nThe next step is to create a chat client which is what we needed the token for. I’m going to use [twilio-chat](https://www.npmjs.com/package/twilio-chat) to connect and work with the [Twilio SDK](http://media.twiliocdn.com/sdk/js/chat/releases/4.0.0/docs/index.html). Let’s install and test it:\n\n```sh\nyarn add twilio-chat events\n```\n\nThen, we’ll create a getToken method for handling communication with our token server.\n\n```js\nconst getToken = (username) =\u003e\n  axios.get(`http://localhost:3001/token/${username}`).then((twilioUser) =\u003e twilioUser.data.jwt);\n```\n\nNow, we should create the Twilio Chat Client instance with a token by calling [create(token)](http://media.twiliocdn.com/sdk/js/chat/releases/4.0.0/docs/Client.html#.create__anchor). Also, we have two events to help manage our token expiration: tokenAboutToExpire and tokenExpired.\n\nLet’s create a twilio-service.js file to prevent the repeat initializing of the Twilio Chat Client across all screens. We’ll create and store a single Twilio service client instance and use it on each screen.\n\n```js\nimport { Client } from 'twilio-chat';\n\nexport class TwilioService {\n  static serviceInstance;\n  static chatClient;\n\n  // create a single service instance\n  static getInstance() {\n    if (!TwilioService.serviceInstance) {\n      TwilioService.serviceInstance = new TwilioService();\n    }\n    return TwilioService.serviceInstance;\n  }\n\n  // use chat client if don't have instance, create a new chat client\n  async getChatClient(twilioToken) {\n    if (!TwilioService.chatClient \u0026\u0026 !twilioToken) {\n      throw new Error('Twilio token is null or undefined');\n    }\n    if (!TwilioService.chatClient \u0026\u0026 twilioToken) {\n      return Client.create(twilioToken).then((client) =\u003e {\n        TwilioService.chatClient = client;\n        return TwilioService.chatClient;\n      });\n    }\n    return Promise.resolve().then(() =\u003e TwilioService.chatClient);\n  }\n\n  // manage our token expiration\n  addTokenListener(getToken) {\n    if (!TwilioService.chatClient) {\n      throw new Error('Twilio client is null or undefined');\n    }\n    TwilioService.chatClient.on('tokenAboutToExpire', () =\u003e {\n      getToken().then(TwilioService.chatClient.updateToken);\n    });\n\n    TwilioService.chatClient.on('tokenExpired', () =\u003e {\n      getToken().then(TwilioService.chatClient.updateToken);\n    });\n    return TwilioService.chatClient;\n  }\n  \n  // gracefully shutting down library instance.\n  clientShutdown() {\n    TwilioService.chatClient?.shutdown();\n    TwilioService.chatClient = null;\n  }\n}\n```\n\nLast, I’ll create chat-create-screen.js with the following code:\n\n```js\nexport function ChatCreateScreen() {\n  const [channelName, setChannelName] = useState('');\n  const [loading, setLoading] = useState(false);\n\n  const onCreateOrJoin = () =\u003e {\n    setLoading(true);\n    TwilioService.getInstance()\n      .getChatClient()\n      .then((client) =\u003e\n        client\n          .getChannelByUniqueName(channelName)\n          .then((channel) =\u003e (channel.channelState.status !== 'joined' ? channel.join() : channel))\n          .catch(() =\u003e\n            client.createChannel({ uniqueName: channelName, friendlyName: channelName }).then((channel) =\u003e channel.join()),\n          ),\n      )\n      .then(() =\u003e showMessage({ message: 'You have joined.' }))\n      .catch((err) =\u003e showMessage({ message: err.message, type: 'danger' }))\n      .finally(() =\u003e setLoading(false));\n  };\n\n  return (\n    \u003cView style={styles.screen}\u003e\n      \u003cImage style={styles.logo} source={images.message} /\u003e\n      \u003cTextInput\n        value={channelName}\n        onChangeText={setChannelName}\n        style={styles.input}\n        placeholder=\"Channel Name\"\n        placeholderTextColor={colors.ghost}\n      /\u003e\n      \u003cTouchableOpacity style={styles.button} onPress={onCreateOrJoin}\u003e\n        \u003cText style={styles.buttonText}\u003eCreate Or Join\u003c/Text\u003e\n      \u003c/TouchableOpacity\u003e\n      {loading \u0026\u0026 \u003cLoadingOverlay /\u003e}\n    \u003c/View\u003e\n  );\n}\n```\n\nOnce the chat client is initialized, we can create a new chat channel with [createChannel({ uniqueName, friendlyName })](http://media.twiliocdn.com/sdk/js/chat/releases/4.0.0/docs/Client.html#createChannel__anchor) or join an existing channel with the join() method. To join an existing channel, we have to get the channel from Twilio by using the [getChannelByUniqueName()](http://media.twiliocdn.com/sdk/js/chat/releases/4.0.0/docs/Client.html#getChannelByUniqueName__anchor) method and passing the room name to it.\n\nIf the channel doesn’t exist, an exception will be thrown. If it does exist, the method will return the channel resource, and from there, the channel can be joined.\n\n\u003cp\u003e\n  \u003cimg width=\"800\"src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/chat-create-screen.png\"\u003e\n\u003c/p\u003e\n\n## Chat List Screen\n\nI’m going to show all of my subscribed channels on the ChatListScreen. As a user, I want to work with updated channels when I join or create a new channel on the CreateChannelScreen. Therefore, we need to store channels globally in Redux or React Context. Redux is too complicated for our simple app, so we’ll use React Context.\n\nLet’s create our app-context.js with the following code:\n\n```js\nimport React, { useState, useContext, createContext } from 'react';\n\nconst defaultInitialState = { channels: [], updateChannels: () =\u003e {} };\n\nconst AppContext = createContext(defaultInitialState);\n\nexport function useApp() {\n  return useContext(AppContext);\n}\n\nexport function AppProvider({ children }) {\n  const [channels, setChannels] = useState([]);\n\n  return \u003cAppContext.Provider value={{ channels, updateChannels: setChannels }}\u003e{children}\u003c/AppContext.Provider\u003e;\n}\n```\n\nAppContext stores a list of channels and the updateChannels method. Hence we can get all of the channels:\n\n```sh\nconst { channels, updateChannels } = useApp();\n```\n\nNow, our chat-list-screen.js:\n\n```js\nexport function ChatListScreen({ navigation, route }) {\n  const { username } = route.params;\n  const { channels, updateChannels } = useApp();\n  const channelPaginator = useRef();\n\n  useLayoutEffect(() =\u003e {\n    navigation.setOptions({\n      headerRight: () =\u003e (\n        \u003cTouchableOpacity style={styles.addButton} onPress={() =\u003e navigation.navigate(routes.ChatCreat.name)}\u003e\n          \u003cText style={styles.addButtonText}\u003e+\u003c/Text\u003e\n        \u003c/TouchableOpacity\u003e\n      ),\n    });\n  }, [navigation]);\n\n  const setChannelEvents = useCallback(\n    (client) =\u003e {\n      client.on('messageAdded', (message) =\u003e {\n        updateChannels((prevChannels) =\u003e\n          prevChannels.map((channel) =\u003e\n            channel.id === message.channel.sid ? { ...channel, lastMessageTime: message.dateCreated } : channel,\n          ),\n        );\n      });\n      return client;\n    },\n    [updateChannels],\n  );\n\n  const getSubscribedChannels = useCallback(\n    (client) =\u003e\n      client.getSubscribedChannels().then((paginator) =\u003e {\n        channelPaginator.current = paginator;\n        const newChannels = TwilioService.getInstance().parseChannels(channelPaginator.current.items);\n        updateChannels(newChannels);\n      }),\n    [updateChannels],\n  );\n\n  useEffect(() =\u003e {\n    getToken(username)\n      .then((token) =\u003e TwilioService.getInstance().getChatClient(token))\n      .then(() =\u003e TwilioService.getInstance().addTokenListener(getToken))\n      .then(setChannelEvents)\n      .then(getSubscribedChannels)\n      .catch((err) =\u003e showMessage({ message: err.message, type: 'danger' }))\n      .finally(() =\u003e setLoading(false));\n\n    return () =\u003e TwilioService.getInstance().clientShutdown();\n  }, [username, setChannelEvents, getSubscribedChannels]);\n\n  return (\n    \u003cView style={styles.screen}\u003e\n      \u003cFlatList\n        data={channels}\n        keyExtractor={(item) =\u003e item.id}\n        renderItem={({ item }) =\u003e (\n          \u003cChatListItem\n            channel={item}\n            onPress={() =\u003e navigation.navigate(routes.ChatRoom.name, { channelId: item.id, identity: username })}\n          /\u003e\n        )}\n      /\u003e\n    \u003c/View\u003e\n  );\n}\n```\n\nFirst, we retrieve the token and create an instance of the Twilio Chat client. Then, we get the current list of all of our subscribed channels by using the getSubscribedChannels() method and storing them in the global React Context.\n\nTwilio doesn’t give you the feature to sort the channel list based on a most recent message. Your best bet is loading all of the channels into an array and sorting them yourself.\n\nTherefore I subscribed to the messageAdded event, which fires when a new message has been added to the channel on the server because I want to sort the channel list by the last-message time. So when someone messages via the chat, we’ll update the last-message time of the specific channel.\n\n\u003cp\u003e\n  \u003cimg width=\"800\"src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/chat-list-screen.png\"\u003e\n\u003c/p\u003e\n\n## Chat Room Screen\n\nI’m going to use [react-native-gifted-chat](https://github.com/FaridSafi/react-native-gifted-chat) for creating a chat room of the channel. react-native-gifted-chat is the most complete and easy-to-use chat UI for React Native.\n\nLet’s install it:\n\n```sh\nyarn add react-native-gifted-chat\n```\n\nFirst we have to get a Twilio Chat client or create one if one doesn’t exist. Then we need to get the specific channel using getChannelBySid(channelSid).\n\nIf we get the channel, we can get all of the messages from the channel by using the getMessages() method.\n\nAlso, I’ll subscribe to the messageAdded event, which fires when a new message has been added to the channel and updates our chat.\n\nLet’s create chat-room-screen.js:\n\n```js\nexport function ChatRoomScreen({ route }) {\n  const { channelId, identity } = route.params;\n  const [messages, setMessages] = useState([]);\n  const chatClientChannel = useRef();\n  const chatMessagesPaginator = useRef();\n\n  const setChannelEvents = useCallback((channel) =\u003e {\n    chatClientChannel.current = channel;\n    chatClientChannel.current.on('messageAdded', (message) =\u003e {\n      const newMessage = TwilioService.getInstance().parseMessage(message);\n      const { giftedId } = message.attributes;\n      if (giftedId) {\n        setMessages((prevMessages) =\u003e prevMessages.map((m) =\u003e (m._id === giftedId ? newMessage : m)));\n      } else {\n        setMessages((prevMessages) =\u003e [newMessage, ...prevMessages]);\n      }\n    });\n    return chatClientChannel.current;\n  }, []);\n\n  useEffect(() =\u003e {\n    TwilioService.getInstance()\n      .getChatClient()\n      .then((client) =\u003e client.getChannelBySid(channelId))\n      .then((channel) =\u003e setChannelEvents(channel))\n      .then((currentChannel) =\u003e currentChannel.getMessages())\n      .then((paginator) =\u003e {\n        chatMessagesPaginator.current = paginator;\n        const newMessages = TwilioService.getInstance().parseMessages(paginator.items);\n        setMessages(newMessages);\n      })\n      .catch((err) =\u003e showMessage({ message: err.message, type: 'danger' }))\n  }, [channelId, setChannelEvents]);\n\n  const onSend = useCallback((newMessages = []) =\u003e {\n    const attributes = { giftedId: newMessages[0]._id };\n    setMessages((prevMessages) =\u003e GiftedChat.append(prevMessages, newMessages));\n    chatClientChannel.current?.sendMessage(newMessages[0].text, attributes);\n  }, []);\n    \n  return (\n    \u003cView style={styles.screen}\u003e\n      \u003cGiftedChat\n        messagesContainerStyle={styles.messageContainer}\n        messages={messages}\n        renderAvatarOnTop\n        onSend={(messages) =\u003e onSend(messages)}\n        user={{ _id: identity }}\n      /\u003e\n    \u003c/View\u003e\n  );\n}\n```\n\nI created a method called onSend(). This method will call the SDK method sendMessage() on the channel object and pass the message typed by your user to it.\n\n\u003cp\u003e\n  \u003cimg width=\"800\"src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/chat-room-screen.png\"\u003e\n\u003c/p\u003e\n\n\n## Let’s Demo Our Twilio Chat App\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width=\"600\"src=\"https://github.com/Gapur/react-native-twilio-chat/blob/master/src/assets/example.gif\"\u003e\n\u003c/p\u003e\n\n## Article on Medium\n\n[Build a Twilio-Powered Chat App Using React Native](https://medium.com/better-programming/build-a-twilio-powered-chat-app-using-reactn-ative-2460b7995a30)\n\n## How to contribute?\n\n1. Fork this repo\n2. Clone your fork\n3. Code 🤓\n4. Test your changes\n5. Submit a PR!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgapur%2Freact-native-twilio-chat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgapur%2Freact-native-twilio-chat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgapur%2Freact-native-twilio-chat/lists"}