Tutorial: How to Build a Slack Clone with React Native — Part 2
Tags: reactnative, slackclone, chat, getstream
Published: 2020-10-31 15:01:48

React Native has come a long way since its first release in 2015. In fact, it has enough components, APIs, and supporting libraries in its present state that you can re-create every app that's out there on the Google Play Store or Apple App Store in no time!

In this series, we will demonstrate how you can build a clone of Slack using React Native. Slack is a widely used messaging
platform for workplaces, and it's quite feature-rich. React Native can handle the front-end side of things for us while relying on [Stream Chat](https://getstream.io/chat/), which is equally seamless, for the back-end.

In April 2020, Stream published a [Part 1](https://getstream.io/blog/slack-clone-with-stream-chat-part-1/) of this series. There, we covered how to build Slack-type screens for navigation, message lists, and channel lists. Slack designs have changed quite a lot since then, so we will build a Slack clone from scratch with updated designs and additional features.

We have decided to divide this tutorial into two parts (Part 2 and [Part 3](https://getstream.io/blog/slack-clone-with-stream-chat-part-3/)).

In Part 2 (this tutorial) and 3 of this series, we will focus on the following items:

- Dev Environment Setup
- Project Setup
- Navigation
- Channel List
- Message List
- Action Sheet
- Reaction Picker

> _**Note**: The objective of this tutorial is not intended to help you build a production-ready clone of the Slack application (because it already exists). Rather, this tutorial is a comprehensive guide on how to build real-time chat using UI components provided by Stream's Chat and Messaging API and SDKs._

## Resources 👇

Below are a few links to help you if you get stuck along the way:

- [Official Slack Clone Repo](https://github.com/GetStream/slack-clone-react-native)
- [Official Slack Clone Repo for Expo](https://github.com/GetStream/slack-clone-expo)
- [Documentation for React Navigation](https://reactnavigation.org/)
- [Stream Chat Component Library](https://github.com/GetStream/stream-chat-react-native)

## Quick Test 🥽

If you would like to see the final state of the app in action quickly, clone the following expo example of the slack clone and run it on the emulator or a phone:

{% gist https://gist.github.com/nparsons08/f4d2ed23e943467eddd30d7d43a63ab3 %}

## Step 1: Setup 🛠️

### Dev Environment Setup

Before getting started, make sure you have a development environment setup for react-native. Read the **Installing Dependencies** section of the [official react-native docs](https://reactnative.dev/docs/environment-setup).

### Project Setup

Once you have a dev environment setup, create a new react-native application:

{% gist https://gist.github.com/vishalnarkhede/660c7ddc405124112c1d4b4b82d63329 %}

We will be using the svg format for all the icons that you see across the app. And thus, we are using `react-native-svg-transformer` dependency. Follow the installation steps required for this dependency: https://github.com/kristerkari/react-native-svg-transformer#installation-and-configuration

Slack uses Lato font, which is available free on https://fonts.google.com/. For visual parity, we need to import the font into our app. To do so, create a file named `react-native.config.js` in the project directory and paste the following contents:

{% gist https://gist.github.com/vishalnarkhede/fb09f427d47f94a06324f475b0b9e7d0 %}

You can download Lato font files from the `slack-clone` project [repository](https://github.com/GetStream/slack-clone-react-native/masterand icons from [here](https://github.com/GetStream/slack-clone-react-native/tree/master

You can download the fonts from the Google Fonts website [here](https://fonts.google.com/specimen/Lato). You will see a `Download family` button at the top.

Next, prepare the following directory structure in the root directory of the project:

![Stream Chat Clone - Directory List](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/0061d760c88fbf2489e272f587569651/image10.png)

Run the following command:

{% gist https://gist.github.com/nparsons08/711410db5bca4a95a4b3629765a1b756 %}

Also, change the content of index.js in the root directory to the following:

{% gist https://gist.github.com/vishalnarkhede/e5b272bc7459ea71f1d7a4ebc5ad3eed %}

This completes the setup required for your slack-clone app. You should now be able to run the app with the following command to launch the app on an emulator. Once started, you will see a welcome screen to React Native.

{% gist https://gist.github.com/nparsons08/97f1d0aed10337ca5e447fe270dcd9bb %}

![React Native - Welcome Screen](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/580197719c463804f56f85612f2eba62/image16.png)

## Step 2: Components 🧬

### Utilities

Here's how to add a few essential utilities into your app.

Create the following directories for storing svg icons:

- `src/images/svgs/channel`
- `src/images/svgs/channel-list`
- `src/images/svgs/message`
- `src/images/svgs/profile`
- `src/images/svgs/tab-bar`

Next, download all the svg icons from [GitHub](https://github.com/GetStream/slack-clone-react-native/tree/master/src/images/svgs).

To make it easy to use svg icons across the app, create a separate component, `SVGIcon`, which accepts type, height, and width props and renders the icon on view. Create a component in `src/components/SVGIcon.js`.

{% gist https://gist.github.com/vishalnarkhede/8b46d03414da9e267e1ca9527404560c %}

We will add support for dark mode, so we need to create separate themes for the app. We are using a theme provider from react-navigation for this project. You can check out the documentation to know more about [themes](https://reactnavigation.org/docs/themes/) and [useTheme hook](https://reactnavigation.org/docs/use-theme)

{% gist https://gist.github.com/vishalnarkhede/b1617aa7d763517d87ab9ef640369f75 %}

Next, create a Text component with the Lato font-family, which will used across the app.

{% gist https://gist.github.com/vishalnarkhede/7e22d6f8b08929bd94ae096e25aace45 %}

Copy the rest of the utils from [here](https://github.com/GetStream/slack-clone-react-native/tree/master/src/utils) to `src/utils/` directory. You can read through the code comments about it, but they will make more sense as we use them through this tutorial.

The `utils/useStreamChatTheme.js` file exports a hook, which provides a theme for Stream Chat components. If you are not aware of theming for chat components from [stream-chat-react-native](https://www.npmjs.com/package/stream-chat-react-native) library, please check [this link](https://getstream.io/chat/react-native-chat/tutorial/#custom-styles). We need these styles to change according to the system theme (dark-light), so we have built a hook around this theme object in `src/utils/useStreamChatTheme.js`.

### Basic Navigation

Before we dive into the navigation part, lets first design the screen header and floating action button:

![Basic Bottom Navigation](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/08b7ff184e6d2e34c972a2a2e885540f/header-fab.png)

The `ScreenHeader` component is below. We are using the `useSafeAreaInsets` hook to avoid overlapping content with the top inset of a device such as the iPhone 11.

{% gist https://gist.github.com/vishalnarkhede/5991f2bd8a939c3b0a13436073af95f9 %}

The floating action button, which opens the new message screen (which we will implement later), is as follows:

{% gist https://gist.github.com/vishalnarkhede/8d929cc1c0bbd12820a2ce8df7c66c18 %}

Let's move on to setting up the navigation for our app, similar to Slack. Replace App.js with the following. It's essential to place navigation components at the correct position in the [stack](https://reactnavigation.org/docs/stack-navigator/).

We need screens such as a draft screen or thread screen outside of the bottom tab navigation, so they go at the root level. Screens such as channel searches are a perfect example of [react-navigation modals](https://reactnavigation.org/docs/modal/).

{% gist https://gist.github.com/vishalnarkhede/948553d8872b695923a01e66e8dbf622 %}

If you refresh the app, you should see the following navigation. You can toggle the dark mode by pressing `cmd + shift + a`.

![Basic Navigation](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/dae15067ba07778471c89e7b778436c0/basic-nav-2.gif)

### Channel List Screen

Now, set up the channel list screen. This is similar to how we did it in [Part 1](https://getstream.io/blog/slack-clone-with-stream-chat-part-1/) of the tutorial, with few changes.

Let's outline the important specs that we need to implement:

- Channels to be grouped by
- Unread channels and unread conversations ( `directMessagingConversations`)
- Channels
- Direct Message (`directMessagingConversations`)

Channels in our case are defined by a conversation with a non-empty name. `directMessagingConversations` are defined by conversations without a name.

- Unread channel labels are bold
- In case of one-to-one conversation (a subgroup of `directMessagingConversations`), users have a presence indicator next to their name — green if they are online, otherwise hollow circles.

Add the following files to the project:

{% gist https://gist.github.com/vishalnarkhede/04b7846035e683448f2aa786fa50d562 %}

The `useWatchedChannels` hook makes two calls to the `queryChannels` API to:

- Fetch all group channels (conversations with a name)
- Fetch all direct messages (conversations without a name)

Once fetched, results are sorted into three variables:

1. `unreadChannels`
2. `readChannels`
3. `directMessagingConversations`

We can reuse the queried channel data in other screens; thus, we are caching it in `CacheService`. To keep things simple, use an object for caching. You can use redux or some other state management service for this purpose.

Reload the app, and you should see the `ChannelList` screen working in the app.

### Channel Screen

Let's start by building the header for the channel screen.

![Channel Header](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/e5bdec35805604a42dbe80e430400af9/Screenshot-2020-10-31-at-22-10-38.png)

Create a new component for channel header:

{% gist https://gist.github.com/vishalnarkhede/03e83ab75580c176b10a082f8d5a3cb9 %}

Now we can build a `ChannelScreen` component with the following functionality:

- Renders a MessageList component, with customized UI component to be displayed for message bubble - `MessageSlack` ([🔗](<https://github.com/GetStream/stream-chat-react-native/wiki/Cookbook-(2.x.x)#how-to-customize-message-component>))
- Renders a MessageInput component, with customized UI component to be shown for input box - `InputBox` ([🔗](<https://github.com/GetStream/stream-chat-react-native/wiki/Cookbook-(2.x.x)#how-to-change-layout-of-messageinput-message-text-input-box-component>)). Copy the updated copy of component [InputBox](https://github.com/GetStream/slack-clone-react-native/blob/master/src/components/InputBox.js) to src/components directory.
- When the user navigates away from `ChannelScreen`, we store the user object in [AsyncStorage](https://react-native-async-storage.github.io/async-storage/docs/install/) as well as the message last type into the input box. This is required for the `DraftMessages` screen. In our code sample, we created the [AsyncStore](https://github.com/GetStream/slack-clone-react-native/blob/master/src/utils/AsyncStore.js) utility, which is an interface between `AsyncStorage` and app-level code, to help avoid boilerplate code around stringifying and parsing of values.
- When a user lands on the `ChannelScreen`, check if a draft message exists in `AsyncStorage` for the current channel. If yes, set that message as the initial value of the input box.

{% gist https://gist.github.com/vishalnarkhede/86205a462800473da81442020dd1b225 %}

`MessageSlack` component is as follows:

{% gist https://gist.github.com/vishalnarkhede/d4b5020667e3f1722d728e9d8a64f7fa %}

If you have read Part 1 of this tutorial, then you will recognize most of the props on MessageSimple component. Copy updated code for these components to `src/components` directory:

- [MessageAvatar](https://github.com/GetStream/slack-clone-react-native/blob/master/src/components/MessageAvatar.js)
- [MessageHeader](https://github.com/GetStream/slack-clone-react-native/blob/master/src/components/MessageHeader.js)
- [MessageText](https://github.com/GetStream/slack-clone-react-native/blob/master/src/components/MessageText.js)
- [UrlPreview](https://github.com/GetStream/slack-clone-react-native/blob/master/src/components/UrlPreview.js)
- [Giphy](https://github.com/GetStream/slack-clone-react-native/blob/master/src/components/Giphy.js)

The `MessageFooter`, `ReactionPicker`, and `MessageActionSheet` are the new elements introduced in this version of the Slack clone.

The `MessageFooter` component now provides a Slack type reaction selector (shown in the following screenshot). `ReactionPicker` is just another component, whose visibility is controlled by two handles — `openReactionPicker` and `dismissReactionPicker`. These two functions are available on MessageSimple and all its children as props.

![Slack Type Reaction Picker](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/986c62fe9575b3344df2c272145d249d/reaction-picker.gif)

{% gist https://gist.github.com/vishalnarkhede/73d136ba481deae0f0559f8778fa2675 %}

Notice the [react-native-haptics](https://developer.aliyun.com/mirror/npm/package/react-native-haptic) dependency add additional haptic feedback on touch on an iOS device.

The `ActionSheet` prop was introduced in [stream-chat-react-native@2.0.0](https://www.npmjs.com/package/stream-chat-react-native) on the `MessageSimple` component. In our case, we need to build a Slack type action sheet, which is quite different than what `stream-chat-react-native` provides out-of-the-box:

![React Native Action Sheet](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/94176a864a13a93445067f1dfc9347ee/Screenshot-2020-11-01-at-10-07-18.png)

Code for `MessageActionSheet` looks like the following:

{% gist https://gist.github.com/vishalnarkhede/459950d0b16a37ed3ca51a7f056521bf %}

Now assign the ChannelScreen component to its respective `HomeStack.Screen` in `App.js`.

{% gist https://gist.github.com/nparsons08/8c697e9d8d8df7c0fe3476af62de62d6 %}

If you reload the app, you should see the `ChannelScreen` working correctly.

## Congratulations! 👏

You've completed Part 2 of our tutorial on building a Slack clone using the [Stream’s Chat](https://getstream.io/chat/) API with [React Native](https://www.npmjs.com/package/stream-chat-react-native). In [Part 3](https://getstream.io/blog/slack-clone-with-stream-chat-part-3/) of the tutorial, we cover various search screens on the Slack clone app and threads screen.

Happy coding!