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, which is equally seamless, for the back-end.
In April 2020, Stream published a 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).
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
- Official Slack Clone Repo for Expo
- Documentation for React Navigation
- Stream Chat Component Library
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:
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.
Project Setup
Once you have a dev environment setup, create a new react-native application:
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:
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. You will see a Download family
button at the top.
Next, prepare the following directory structure in the root directory of the project:
Run the following command:
Also, change the content of index.js in the root directory to the following:
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.
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.
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
.
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 and useTheme hook
Next, create a Text component with the Lato font-family, which will used across the app.
Copy the rest of the utils from here 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 library, please check this link. 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:
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.
The floating action button, which opens the new message screen (which we will implement later), is as follows:
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.
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.
If you refresh the app, you should see the following navigation. You can toggle the dark mode by pressing cmd + shift + a
.
Channel List Screen
Now, set up the channel list screen. This is similar to how we did it in 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:
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:
unreadChannels
readChannels
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.
Create a new component for channel header:
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
(🔗) - Renders a MessageInput component, with customized UI component to be shown for input box -
InputBox
(🔗). Copy the updated copy of component InputBox to src/components directory. - When the user navigates away from
ChannelScreen
, we store the user object in AsyncStorage as well as the message last type into the input box. This is required for theDraftMessages
screen. In our code sample, we created the AsyncStore utility, which is an interface betweenAsyncStorage
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 inAsyncStorage
for the current channel. If yes, set that message as the initial value of the input box.
MessageSlack
component is as follows:
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:
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.
Notice the react-native-haptics dependency add additional haptic feedback on touch on an iOS device.
The ActionSheet
prop was introduced in stream-chat-react-native@2.0.0 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:
Code for MessageActionSheet
looks like the following:
Now assign the ChannelScreen component to its respective HomeStack.Screen
in App.js
.
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 API with React Native. In Part 3 of the tutorial, we cover various search screens on the Slack clone app and threads screen.
Happy coding!
Top comments (1)
I did it with custom chat app now I want to change Input filed with emoji button and that emoji keyboard need to open and I am using 3.0.0 version!