React Chat Tutorial
The following tutorial shows you how to quickly build chat leveraging Stream's Chat API and the Stream Chat React components. The underlying API is very flexible and allows you to build nearly any type of chat.
Looking for more? We also recently published articles on Stream Chat Push Notifications, Comparing Chat API Pricing; and for feeds please check out our React Native Activity Feeds which details using our Activity Feed React Components. Thanks!
Environment Setup
Confused about "Environment Setup"?
Let us know how we can improve our documentation:
The easiest way to build the chat application from this tutorial is to create a new project using create-react-app
(CRA). CRA builds a boilerplate React application that can be run locally with just a few simple commands. First, if not already installed, globally add CRA from your terminal with the following command:
yarn global add create-react-app
Note: Ensure you have the most recent versions of Node and Yarn installed
If you’re on macOS with Homebrew installed, but don't have Node and/or Yarn, you can run the following command:
brew install node && brew install yarn
Next, run the following commands to create a new React project called chat-example
:
create-react-app chat-example cd chat-example yarn add stream-chat stream-chat-react
Part 1 - WhatsApp / Facebook Messenger Style Chat
Confused about "Part 1 - WhatsApp / Facebook Messenger Style Chat"?
Let us know how we can improve our documentation:
Core React Components
Our React Chat component library includes everything you need to build a fully functioning chat experience, with support for rich messages, reactions, threads, image uploads, videos, and more. This library was designed to enable customers to get an application up and running quickly and efficiently while supporting customization for complex use cases.
Below is a sample of the basic chat components in the React library. Replace the code in src/App.js
with the following snippet and run yarn start
in your terminal to launch the chat application.
import React from 'react'; import { StreamChat } from 'stream-chat'; import { Chat, Channel, ChannelHeader, MessageInput, MessageList, Thread, Window } from 'stream-chat-react'; import 'stream-chat-react/dist/css/index.css'; const chatClient = StreamChat.getInstance('YOUR_API_KEY'); const userToken = 'USER_TOKEN'; chatClient.connectUser( { id: 'USER_ID', name: 'USER_NAME', image: 'https://getstream.io/random_png/?id=USER_ID&name=USER_NAME', }, userToken, ); const channel = chatClient.channel('messaging', 'USER_ID', { // add as many custom fields as you'd like image: 'https://www.drupal.org/files/project-images/react.png', name: 'Talk about React', members: ['USER_ID'], }); const App = () => ( <Chat client={chatClient} theme='messaging light'> <Channel channel={channel}> <Window> <ChannelHeader /> <MessageList /> <MessageInput /> </Window> <Thread /> </Channel> </Chat> ); export default App;
The Chat
and Channel
components are React context providers that pass a variety of values to their children, including UI components, channel state data, and messaging functions.
Note how we create a channel with the channel
method available on the StreamChat
client instance:
- The first argument is the channel type and the second argument the channel ID
- The channel type determines the enabled features and permissions associated with this channel
- The channel ID is a unique reference to this specific channel
Once you have the app running, you’ll notice the following out-of-the-box features:
- User online presence
- Typing indicators
- Message status indicators (sending, received)
- User role configuration
- Emoji support
- Message read indicators
- Threading and message replies
- Message reactions
- URL preview (send a YouTube link to see this in action)
- File upload and preview
- Video playback
- Autocomplete-enabled search on users, emojis, and commands
- Slash commands such as /giphy and /imgur (custom commands are also supported)
- AI-powered spam and profanity moderation
Adding a Channel List
The next example demonstrates how to render a list of channels using our ChannelList
component. We filter our channel query by the messaging
type and only return channels in which the connected user is a member. Also, since the connectUser
method on the StreamChat
client instance is asnychronous, we're going to refactor the websocket connection logic to more adequately handle potential race conditions and prevent our app from attempting to load an unconnected client instance. We also added the LoadingIndicator
component which will render a loading spinner until the StreamChat
client instance has been set.
Update src/App.js
with the following code:
import React, { useEffect, useState } from 'react'; import { StreamChat } from 'stream-chat'; import { Chat, Channel, ChannelHeader, ChannelList, LoadingIndicator, MessageInput, MessageList, Thread, Window } from 'stream-chat-react'; import 'stream-chat-react/dist/css/index.css'; const userToken = 'USER_TOKEN'; const filters = { type: 'messaging', members: { $in: ['USER_ID'] } }; const sort = { last_message_at: -1 }; const App = () => { const [chatClient, setChatClient] = useState(null); useEffect(() => { const initChat = async () => { const client = StreamChat.getInstance('YOUR_API_KEY'); await client.connectUser( { id: 'USER_ID', name: 'USER_NAME', image: 'https://getstream.io/random_png/?id=USER_ID&name=USER_NAME', }, userToken, ); setChatClient(client); }; initChat(); }, []); if (!chatClient) { return <LoadingIndicator />; } return ( <Chat client={chatClient} theme='messaging light'> <ChannelList filters={filters} sort={sort} /> <Channel> <Window> <ChannelHeader /> <MessageList /> <MessageInput /> </Window> <Thread /> </Channel> </Chat> ); }; export default App;
Note: The ChannelList
component automatically sets the channel
property of the Channel
component
Customizing UI Components
While all of the React components in the library render with default styling, you can also pass via props your own custom UI components to adjust the look and feel to meet your design specifications. In the below example we will create custom ChannelPreview
and Message
components.
Note: All custom UI components that override the defaults receive the same props as their default counterparts
Update src/App.js
with the following code:
import React, { useEffect, useState } from 'react'; import { StreamChat } from 'stream-chat'; import { Chat, Channel, ChannelHeader, ChannelList, LoadingIndicator, MessageInput, MessageList, Thread, Window } from 'stream-chat-react'; import 'stream-chat-react/dist/css/index.css'; const userToken = 'USER_TOKEN'; const filters = { type: 'messaging', members: { $in: ['USER_ID'] } }; const sort = { last_message_at: -1 }; const CustomChannelPreview = (props) => { const { channel, setActiveChannel } = props; const { messages } = channel.state; const messagePreview = messages[messages.length - 1]?.text.slice(0, 30); return ( <div onClick={() => setActiveChannel(channel)} style={{ margin: '12px' }}> <div>{channel.data.name || 'Unnamed Channel'}</div> <div style={{ fontSize: '14px' }}>{messagePreview}</div> </div> ); }; const CustomMessage = (props) => ( <div> <b style={{ marginRight: '4px' }}>{props.message.user.name}</b> {props.message.text} </div> ); const App = () => { const [chatClient, setChatClient] = useState(null); useEffect(() => { const initChat = async () => { const client = StreamChat.getInstance('YOUR_API_KEY'); await client.connectUser( { id: 'USER_ID', name: 'USER_NAME', image: 'https://getstream.io/random_png/?id=USER_ID&name=USER_NAME', }, userToken, ); setChatClient(client); }; initChat(); }, []); if (!chatClient) { return <LoadingIndicator />; } return ( <Chat client={chatClient} theme='messaging light'> <ChannelList filters={filters} sort={sort} Preview={CustomChannelPreview} /> <Channel> <Window> <ChannelHeader /> <MessageList Message={CustomMessage} /> <MessageInput /> </Window> <Thread /> </Channel> </Chat> ); }; export default App;
Custom Message Attachment Type
For the last messaging type example, we're going to create a custom Attachment
component that renders different UI if the attachment is of type 'product'
. To illustrate this functionality, we've set up the app to automatically send a message with a custom attachment on mount. After the user connects, we make a query to fetch the channel we created in the first step and then send a message with the product attachment included. The custom Attachment
component then renders in the MessageList
.
Update src/App.js
with the following code:
import React, { useEffect, useState } from 'react'; import { StreamChat } from 'stream-chat'; import { Attachment, Chat, Channel, ChannelHeader, ChannelList, LoadingIndicator, MessageInput, MessageList, Thread, Window, } from 'stream-chat-react'; import 'stream-chat-react/dist/css/index.css'; const userToken = 'USER_TOKEN'; const filters = { type: 'messaging', members: { $in: ['USER_ID'] } }; const sort = { last_message_at: -1 }; const attachments = [ { image: 'https://images-na.ssl-images-amazon.com/images/I/71k0cry-ceL._SL1500_.jpg', name: 'iPhone', type: 'product', url: 'https://goo.gl/ppFmcR', }, ]; const CustomAttachment = (props) => { const { attachments } = props; const [attachment] = attachments || []; if (attachment?.type === 'product') { return ( <div> Product: <a href={attachment.url} rel='noreferrer'> <img alt='custom-attachment' height='100px' src={attachment.image} /> <br /> {attachment.name} </a> </div> ); } return <Attachment {...props} />; }; const App = () => { const [chatClient, setChatClient] = useState(null); useEffect(() => { const initChat = async () => { const client = StreamChat.getInstance('YOUR_API_KEY'); await client.connectUser( { id: 'USER_ID', name: 'USER_NAME', image: 'https://getstream.io/random_png/?id=USER_ID&name=USER_NAME', }, userToken, ); const [channelResponse] = await client.queryChannels(filters, sort); await channelResponse.sendMessage({ text: 'Your selected product is out of stock, would you like to select one of these alternatives?', attachments, }); setChatClient(client); }; initChat(); }, []); if (!chatClient) { return <LoadingIndicator />; } return ( <Chat client={chatClient} theme='messaging light'> <ChannelList filters={filters} sort={sort} /> <Channel Attachment={CustomAttachment}> <Window> <ChannelHeader /> <MessageList /> <MessageInput /> </Window> <Thread /> </Channel> </Chat> ); }; export default App;
The docs for the React components are available here. If you are looking to build a more complex chat application, please view our API docs or check out the source code for the chat demos on our website.
Part 2 - Livestream Style Chat
Confused about "Part 2 - Livestream Style Chat"?
Let us know how we can improve our documentation:
For the next example, we can customize the original code snippet to work well for a livestream style chat application. In livestream apps, the user interface tends to be more compact and message seen/read states can get noisy as volume increases. For this reason, we've changed the channel type and are using the VirtualizedMessageList
component, which handles list virtualization out-of-the-box and manages memory build-up.
Update src/App.js
with the following code to see a simple livestream example:
import React from 'react'; import { StreamChat } from 'stream-chat'; import { Chat, Channel, ChannelHeader, MessageInput, MessageInputSmall, VirtualizedMessageList, Window } from 'stream-chat-react'; import 'stream-chat-react/dist/css/index.css'; const chatClient = StreamChat.getInstance('YOUR_API_KEY'); const userToken = 'USER_TOKEN'; chatClient.connectUser( { id: 'USER_ID', name: 'USER_NAME', image: 'https://getstream.io/random_png/?id=USER_ID&name=USER_NAME', }, userToken, ); const channel = chatClient.channel('livestream', 'spacex', { image: 'https://goo.gl/Zefkbx', name: 'SpaceX launch discussion', }); const App = () => ( <Chat client={chatClient} theme='livestream dark'> <Channel channel={channel}> <Window> <ChannelHeader live /> <VirtualizedMessageList /> <MessageInput Input={MessageInputSmall} focus /> </Window> </Channel> </Chat> ); export default App;
There are a few important differences compared to the first example:
- We're using the
livestream
channel type, which disables typing events and seen/read states - We set theme to
‘livestream dark’
, which enables dark mode for the livestream channel type - We're using the
VirtualizedMessageList
component, which supports the high message volume unique to livestream events - We're using the
MessageInputSmall
UI component in ourMessageList
, which is a slimmed down version of our standard input that still supports emojis and other text input features
As a next step, check out the source code for the chat demos on our website to view more detailed integrations and complex use-cases involving our React components.
Final Thoughts
In this chat app tutorial we built a fully functioning React messaging app with our React SDK component library. We also showed how easy it is to customize the behavior and the style of the React chat app components with minimal code changes.
Both the chat SDK for React and the API have plenty more features available to support more advanced use-cases such as push notifications, content moderation, rich messages and more. Please check out our React Native tutorial too. If you want some inspiration for your app, download our free chat interface UI kit.
Give us Feedback!
Did you find this tutorial helpful in getting you up and running with React for adding chat to your project? Either good or bad, we’re looking for your honest feedback so we can improve.