Tutorial: How to Build a Slack Clone with React Native — Part 1
Tags: reactnative, chat, javascript, tutorial
Published: 2020-04-24 10:43:57

[React Native](https://reactnative.dev/) has a significant footprint in the mobile development world. And with every new release, it gets better and better in terms of development speed and performance. Building a chat application used to be a massive chunk of work, but with the power of react-native and [Stream Chat](https://getstream.io/chat/), it's possible to create a messaging app within minutes.

In this tutorial, we will build a clone of Slack, a messaging platform for workplaces. The Slack application comes with plenty of features. In this part of our tutorial, we will cover Slack’s following UI/UX features:

- Channel list navigation
- Input box
- Message row
- Reaction list
- Giphy cards
- Enriched URL previews

The result will look like the following:

![Stream Chat - Final Slack Clone - Preview](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/846c9491c65e1d941902f5276c597b7f/final.png?auto=compress%2Cformat&fit=scale&h=1237&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

> **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). Instead, this tutorial will serve as a go-to guide to help you understand how you can build real-life chat using UI components provided by Stream's Chat and Messaging API and SDKs.

If you feel lost during the tutorial, the  following resources will be helpful:

- [React Native Chat Tutorial](https://getstream.io/chat/react-native-chat/tutorial/)
- [Stream’s React Native Chat Components](https://getstream.github.io/stream-chat-react-native/)

> **Note** If you want to send a message to the app, to check if the real-time feature is working or not, use this [CodePen](https://codepen.io/vishtree/pen/YzyyEBG?editors=0010).

## Resources 👇

Here 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/tree/slack-clone-1.0)
- [Official Slack Clone Repo for Expo](https://github.com/GetStream/slack-clone-expo/tree/slack-clone-1.0)
- [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, please 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, please make sure you have a development environment setup for react-native. Please 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/0d5baabc8eb3241a2c2acdf0f08c921b %}

Slack uses a Lato font, which is freely available 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/blob/slack-clone-1.0/src/fonts) and icons from [here](https://github.com/GetStream/slack-clone-react-native/blob/slack-clone-1.0/src/images).

Alternatively, you can download the fonts from the [Google Fonts website](https://fonts.google.com/specimen/Lato). You will see a button titled `Download family` 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?auto=compress%2Cformat&fit=scale&h=2048&ixlib=php-1.2.1&w=1536&wpsize=2048x2048)

Please run the following command at this step:

{% gist https://gist.github.com/nparsons08/43f6f7431647af0d06134473a27357fe %}

With these steps in place, 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/3a27864f186f54df45893d9875323cc5 %}

![React Native - Welcome Screen](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/580197719c463804f56f85612f2eba62/image16.png?auto=compress%2Cformat&fit=scale&h=2048&ixlib=php-1.2.1&w=1130&wpsize=2048x2048)

## Step 2: Components 🏗️

### Basic Navigation Drawer
Let's first create a basic drawer navigation in our app. Replace the content of `App.js` with the following code:

{% gist https://gist.github.com/vishalnarkhede/0095ad6050a8fdefd47dfefeffe9a844 %}

After you’ve completed this, you should see the essential Slack-like drawer navigation if you check your emulator.

![Stream Slack Clone - Getting Started](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/479d3ef4c8e071940d19d9ed06c5af76/base.png?auto=compress%2Cformat&fit=scale&h=1807&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

## Channel List Navigation 🧭

Now let's create a channel list navigation and add it to the drawer that we just created. For the Slack navigation drawer, some essential UI elements that we will focus on are the following:

- Channels are grouped by
 - Unread channels
 - Channels (read channels)
 - Direct messages - this is perfect use case of [SectionList](https://reactnative.dev/docs/sectionlist) in react-native
- Unread channel labels are bold
- Direct message users have a presence indicator next to their name - green if they are online, otherwise hollow circles.

Let's create a file named `src/components/ChannelList.js`. You can copy the contents of the following code snippet into your newly created file:

{% gist https://gist.github.com/vishalnarkhede/46f946175ab6b06fec48200717411265 %}

Additionally,  replace the `ChannelListDrawer` component in `App.js` with the following:

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

If you are familiar with react-native, this piece of code should be pretty straightforward. We have added a `SectionList` component with three sections: unread, channels, direct messages. You should see the following in your app so far:

![Stream Chat Clone - Base](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/d4e3b42a49132c9495dea567768cf5fd/image5.png?auto=compress%2Cformat&fit=scale&h=2048&ixlib=php-1.2.1&w=1162&wpsize=2048x2048)

Now let's populate the `SectionList` with some channels. As I mentioned earlier in the tutorial, we are going to use Stream’s chat infrastructure. 

Let's start by creating a Stream Chat client in `App.js` and passing it as a prop to the `ChannelList` component.

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

We have also added a prop function named `changeChannel`, which takes care of opening the channel screen and passing the provided channel ID to it. We will use this function as an `onPress` handler for the `ChannelListItem`.

Now let’s create a hook in `ChannelList.js` file, which takes care of querying channels. Later, we will update them in real-time when new messages arrive, or we move messages between groups.

If you are not familiar with React hooks, here are some great resources to get started:

- [Introduction to React Hooks](https://reactjs.org/docs/hooks-intro.html)
- [Easy to Understand React Hook Recipes](https://usehooks.com)

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

This hook queries the channels using the Stream client.  It sorts them into three categories, which are returned as state variables: `unreadChannels`, `readChannels`, `oneOnOneConversations`

The `renderChannelListItem` function currently returns `<Text>{channel.id}</Text>`, which displays the ID of the channel. Let's create a proper UI for this item that resembles Slack.

Create a new component in a separate file named `src/components/ChannelListItem.js`.

{% gist https://gist.github.com/vishalnarkhede/43dfffc6e1d3001de472a7b05b230fc2 %}

This component will ensure different styles based on whether it's a group channel or one-on-one conversation, or if it's an unread channel. It will also check whether or not it contains user mentions.

Now let's use our `ChannelListItem` component in the `ChannelList` component’s `SectionList`.

{% gist https://gist.github.com/vishalnarkhede/6fd54659cc3bcffef68560caa8d69f16 %}

As you can note here, I have supplied `isUnread: true` to unread section data. This way, I can tell the `renderChannelRow` function if the current channel to render is unread or not. 

It's not necessary since you can quickly get an unread count of the channel in `renderChannelRow` using `channel.unreadCount()` to decide if it's read or unread. But it's just a way to avoid extra calls to `channel.countUnread()`, which essentially loops through messages.

If you reload your app, you should see a few channels populated in the channels list, as shown in the screenshot below:

![Stream Chat Slack Clone - Notification Indicator](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/8cb0c6563d675134b95c3bf474fc70f3/image3.png?auto=compress%2Cformat&fit=scale&h=2048&ixlib=php-1.2.1&w=1130&wpsize=2048x2048)

So far, `ChannelList` works fine, but you will notice that it's not real-time. If a message is sent on some channel by another user, it won’t reflect on your `ChannelList`. We need to implement event handlers in our `useWatchedChannels` hook for this purpose.

You can find detailed docs about Stream events [here](https://getstream.io/chat/docs/event_listening/).

We are going to handle two events for tutorial purpose, but you can experiment with as many events as you want:

1. `message.new` - this event tells us that there is a new message on some channel (channel data is included in the event object). In this case, we want to move the channel from either `readChannels` or `oneOnOneConversations` to `unreadChannels`.
2. `message.read` - this event tells us that some channel (data available in event object) was marked as read. In this case, we want to move the channel from `unreadChannels`  to either `readChannels` or `oneOnOneConversations`.

Replace the `useWatchedChannels` hook code with the following updated code:

{% gist https://gist.github.com/vishalnarkhede/5111c087524dc4e457198ecbcd451e62 %}

We have added another `useEffect` hook here, which adds an event listener to our stream client and takes care of removing the listener when the component unmounts. The `handleEvent` is an event handler that will take some action based on which event is received.

> As an exercise, you can try adding handlers for other events such as `user.presence.changed`, `channel.updated` or `channel.deleted`

Now try sending a message to some channel from this [CodePen](https://codepen.io/vishtree/pen/YzyyEBG?editors=0010), (which uses the user `Tommaso`) and you should see the channel with a new message moving to the unread section.

Now the last thing we need to take care of is the `onclick` handler for `ChannelListItem`. When an item is selected, we need to update the channel in the `ChannelScreen`.

This concludes our `ChannelList` component. If you send a message to a channel in this list, you will see the event handler doing its job of updating the list UI accordingly.

## Channel Screen 📱

Let’s start by building the following channel header shown below:

![Stream Chat Slack Clone - Channel Name](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/8c2cf5ddc8dade54a518b9a0f4b7a2d9/image7.png?auto=compress%2Cformat&fit=scale&h=519&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

Create a new file for header - `src/components/ChannelHeader.js`:

{% gist https://gist.github.com/vishalnarkhede/28425f843ff4f4a507ca0e089a2ddde4 %}

With this, we have added a hamburger icon on the left side of the screen, which, when clicked, will open the navigation drawer.

We are still yet to put this `ChannelHeader` in our `ChannelScreen` component.

Update the `ChannelScreen` component in `App.js` with the following:

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

If you reload your app, you should see an empty channel screen with the header on top:

![Stream Chat Clone - Empty Channel](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/59cde8525ce37464f9e70e3b7f14d894/image8.png?auto=compress%2Cformat&fit=scale&h=2048&ixlib=php-1.2.1&w=1130&wpsize=2048x2048)

Now let's move onto adding `MessageList` and `MessageInput` components to our `ChannelScreen`.

These two components are provided by Stream as part of the [react-native-sdk](https://getstream.io/chat/react-native-chat/tutorial/#stream-app).

Please update the `ChannelScreen` component with the following:

{% gist https://gist.github.com/vishalnarkhede/5208c392b246f10784cd660c4949b22a %}

After this change, you will see messages and an input box at the bottom of our Channel Screen.

![Stream Chat Slack Clone - Reactions](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/73237d41893235543160d739bd8e81eb/reactions.png?auto=compress%2Cformat&fit=scale&h=1865&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

But it doesn’t quite look like Slack messages. So now we need to make changes to make it look like Slack. Here is the list of things in Slack UI that separates it from our current UI in the app.

![Stream Chat Slack Clone - Message](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/9fc79ad7d5559e440959d6cb9346ef79/image4.jpg?auto=compress%2Cformat&fit=scale&h=1940&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

1. The user name is shown at the top of the message
2. Avatars (circular user profile pics next to message) should be square
3. Reactions should be at the bottom of the message
4. Reaction counts should be shown next to each reaction
5. URL preview should have a thick left grey border and its content alignment offset
6. All the messages should be displayed on the left side of the screen
7. GIFs are shown differently in slack channels
8. Date separator between messages should be shown above a grey line
9. Send and attach buttons should be below the input box.

We will tackle these things one by one. Stream’s react-native SDK uses [MessageSimple](https://github.com/GetStream/stream-chat-react-native/blob/master/src/components/MessageSimple/index.js) as the default message component. But you can also use a custom UI component as a message – [reference here](https://github.com/GetStream/stream-chat-react-native/blob/master/docs/cookbook.md#how-to-customize-message-component).

First, let’s add some basic custom theme styles. Let's create a custom message component (named `MessageSlack`) which internally uses MessageSimple with modifications. The `MessageSimple` component offers plenty of customizations. We will create our custom components for the following props, which are supported by the `MessageSimple` component. 

> **Note**: Please check the [cookbook](https://github.com/GetStream/stream-chat-react-native/blob/master/docs/cookbook.md#how-to-customize-message-component) for more examples)

- MessageAvatar
- MessageFooter (which contains reactions)
- MessageHeader (which contains the sender's username)
- MessageText
- UrlPreview (used to display enriched URL preview)
- Giphy (used to show Giphy cards)

Let's create each of these components:

`src/components/MessageSlack.js`
{% gist https://gist.github.com/vishalnarkhede/f7b4bf02de3772c25631f3692c64d024 %}

`src/components/MessageFooter.js`
{% gist https://gist.github.com/vishalnarkhede/e30097aeb24ef1a5d506053b6cda9417 %}

`src/components/MessageHeader.js`
{% gist https://gist.github.com/vishalnarkhede/4d30732665c7fd85dc4e884d48247a5f %}

`src/components/MessageText.js`
{% gist https://gist.github.com/vishalnarkhede/66d0ca4f7bfc0292cc0a96af15fcbda3 %}

`src/components/MessageAvatar.js`
{% gist https://gist.github.com/vishalnarkhede/301e101d51f2fb7e13086bab54e94da3 %}

`src/components/UrlPreview.js`
{% gist https://gist.github.com/vishalnarkhede/a287efc6637a4a3166ebbac14f6d56ba %}

`src/components/Giphy.js`
{% gist https://gist.github.com/vishalnarkhede/49deb8edb23d2ac07bb075718bb125e0 %}

We also need a custom `DateSeparator` component. The default that is used by Stream shows the date in the middle of a spacer/line. However, in the Slack UI, it is shown on top in a grey spacer/line.

`src/components/DateSeparator.js`
{% gist https://gist.github.com/vishalnarkhede/bd682b394c7dc9a606484f97a548b896 %}

Now, after this, all you need to do is pass `MessageSlack` and `DateSeparator` to the `MessageList` component in `App.js.`

{% gist https://gist.github.com/vishalnarkhede/20407f709445ccac9d5a487d0f84a917 %}

If you refresh the app, you will see the UI now has much better parity with the slack UI.

We still need to add some final finishing touches, such as the square avatar. The avatar should be aligned with the top of the message, and messages should not have borders, so we'll need to make some small alignment tweaks as well.

We will take care of them by theming the chat component. Please read the Custom Styles section of Stream’s react-native [chat tutorial](https://getstream.io/chat/react-native-chat/tutorial#custom-styles).

Create a file named `src/stream-chat-theme.js`:

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

Now pass this theme to the `Chat` component in the `ChannelScreen` within App.js, as shown below:

{% gist https://gist.github.com/nparsons08/9af0bbfca4373e012da7eaf5220bdf55 %}

And that's it! You should see beautiful Slack-like messages on the screen. 😺

![Stream Chat Slack Clone](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/3a2354e96a0f5a042d7924078c06f213/final-2.png?auto=compress%2Cformat&fit=scale&h=1865&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

## Input Box 👨‍💻

Now let's move on to the input box at the bottom. The `MessageInput` component (from Stream) accepts `Input` as a custom UI component prop to be shown for the input box. Let's create this custom component in `src/components/InputBox.js`.

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

The following components that we have used in the InputBox are provided by Stream’s react-native SDK, which takes care of plenty of things for us:

- `AutoCompleteInput` - takes care of all input box features such as mentions, sending messages, maintaining enabled/disabled state, etc.
- `SendButton`
- `AttachButton`

All we have done is shuffle around the internal components of the `MessageInput`.

It is important to note here that you must pass the entire prop object to `AutoCompleteInput`, `SendButton`, and `AttachButton`. Hence, all the handlers present in `MessageInput` are accessible to these components.

Now pass this `InputBox` component to `MessageInput` in the `ChannelScreen` component of `App.js`.

{% gist https://gist.github.com/nparsons08/5f7e1bd9156e117eda5aa7f31a6fa78d %}

The final version of the `ChannelScreen` component is as follows:

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

> **Note**:  the extra prop to MessageInput component - `additionalTextInputProps`. That is to modify the placeholder of the input box.

![Stream Chat - Final Slack Clone](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/846c9491c65e1d941902f5276c597b7f/final.png?auto=compress%2Cformat&fit=scale&h=1237&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

## Congratulations! 👏

This concludes part one of our tutorial on building a Slack clone using Stream’s React Native Chat Components. I hope you found this tutorial useful, and I look forward to hearing your feedback.

In the next part of the tutorial – which will be published later – we will cover additional UI components and their functionality, such as:

- Threads
- Channel search
- Action sheets
- Unread message notifications
- And more!

Happy coding!