Creating one-to-one chat with React

The Chatkit React SDK builds on top of the general Chatkit JavaScript SDK to automatically inject Chatkit data and callbacks into your React application. In this guide we will use the withChatkitOneToOne component to build a one-to-one chat. If this doesn’t fit your use case please see the React Getting Started Guide

This SDK is in Beta. We are expecting to make breaking changes in the future. If you have any feedback/suggestions please get in touch.

Project overview

This guide will take you through the basics of using Chatkit with React. At the end of it you will have a working implementation of a one-to-one chat, powered by Chatkit.

The app you will be building

Preparation

Installing dependencies

Clone the following repository which contains a skeleton chat application that has not yet been integrated with Chatkit: github.com/pusher/chatkit-getting-started-react-one-to-one

Then install the Chatkit React SDK:

  • Yarn
  • NPM
1
2
yarn
yarn add @pusher/chatkit-client-react

Creating a Chatkit instance

If you’ve already followed the React getting started then you can re-use the same instance and users and skip to Developing the App.

Head on to dash.pusher.com, and click on the Create button in the Chatkit box. Give your instance a name.

name your instance

Clicking the create button will take you to your instance’s dashboard. Click on the Credentials tab under your instance name, where you’ll see 2 long strings - Instance Locator, and Secret Key.

They are unique for each instance. The instance locator lets your app connect to your Chatkit instance, and you will use the Secret Key on the server to authenticate clients.

your credentials

Creating users with the Instance Inspector

Next, you will need to add some test users to your instance (Alice and Bob). Open the Console tab for your instance on the dashboard and click the CREATE USER button.

screenshot of selecting the 'create user' button in the instance inspector

Create two new users, one with ID alice and name Alice A and the other with ID bob and name Bob B:

screenshot of the instance inspector after both users have been added

Developing the app

Running the app

Open the getting started project in your terminal and run:

  • Yarn
  • NPM
1
yarn start

You should see the login screen of the getting started app open in your browser

Click the "Log in as Alice" button to see the hard-coded conversation already in the application.

screenshot of the getting started app login screen

A word on Token Providers

To make any request to the Chatkit API, you will require a valid token, that is signed by your secret key. You will normally generate these tokens in your backend, using the server SDK.
In the client you can use an instance of TokenProvider. It takes the url of an endpoint to request tokens from. The Chatkit SDK needs one of these tokens to perform any action with the Chatkit API.

For now, we provide you with a Test Token Provider endpoint, that you can enable for testing and development purposes.

To enable the test token provider, you need to check the box in the dashboard, in the Credentials tab, below the instance locator and the key - as you can see in the screenshot below. The dashboard generates you a Test Token Provider endpoint.

You can read more about how Chatkit performs authentication in the docs.

test token provider inspector

Adding a Token Provider to your app

Open src/App.js in your editor and create a newTokenProvider instance. Copy the test token provider URL from the dashboard and paste it in the TokenProvider constructor:

import React from 'react';
import { ChatkitProvider, TokenProvider } from '@pusher/chatkit-client-react';
 
import './App.css';
import Chat from './Chat';
import UserList from './UserList';
import Login from './Login';
import chatkitLogo from './chatkit-logo.svg';
 
const tokenProvider = new TokenProvider({
  url: '<YOUR_TEST_TOKEN_PROVIDER_URL>',
});
 
function App() {
  const urlParams = new URLSearchParams(window.location.search);
  const userId = urlParams.get('userId');
  const otherUserId = urlParams.get('otherUserId');
/* Rest of file truncated */

Connecting to Chatkit

You can connect to Chatkit using the ChatkitProvider. This component will inject your connection to Chatkit into all Chatkit-compatible child components.

You will need to pass the following props to the ChatkitProvider:

  • instanceLocator: A unique string which helps clients find your Chatkit instance. This can be found in the Credentials tab in the dashboard
  • tokenProvider: A reference to the TokenProvider you created in the previous step
  • userId: The ID of the user you would like to connect as. In a real application this would be loaded from your authentication system. In this example, the ID is loaded from a URL query param.
import React from 'react';
import { ChatkitProvider, TokenProvider } from '@pusher/chatkit-client-react';
 
import './App.css';
import Chat from './Chat';
import UserList from './UserList';
import Login from './Login';
import chatkitLogo from './chatkit-logo.svg';
 
const instanceLocator = '<YOUR_INSTANCE_LOCATOR>';
 
const tokenProvider = new TokenProvider({
  url: '<YOUR_TEST_TOKEN_PROVIDER_URL>',
});
 
function App() {
  const urlParams = new URLSearchParams(window.location.search);
  const userId = urlParams.get('userId');
  const otherUserId = urlParams.get('otherUserId');
 
  return (
    <div className="App">
      {userId && otherUserId ? (
        <>
          <div className="App__chatwindow">
            <UserList userId={userId}/>
            <Chat />
            <ChatkitProvider
              instanceLocator={instanceLocator}
              tokenProvider={tokenProvider}
              userId={userId}
            >
              <UserList userId={userId}/>
              <Chat />
            </ChatkitProvider>
          </div>
        </>
      ) : (
        <Login />
 
/* Rest of file truncated */

Selecting a user to talk to

Pass the ID of the user you would like to talk to into the Chat component using the otherUserId prop. This ID is also loaded from a query parameter in this example.

function App() {
  const urlParams = new URLSearchParams(window.location.search);
  const userId = urlParams.get('userId');
  const otherUserId = urlParams.get('otherUserId');
 
  return (
    <div className="App">
      {userId && otherUserId ? (
        <>
          <div className="App__chatwindow">
            <ChatkitProvider
              instanceLocator={INSTANCE_ID}
              tokenProvider={tokenProvider}
              userId={userId}
            >
              <UserList userId={userId}/>
              <Chat />
              <Chat otherUserId={otherUserId} />
            </ChatkitProvider>
          </div>
        </>
      ) : (
        <Login />
      )}
      <div className="App__backdrop">
 
/* Rest of file truncated */

Wrapping your chat component

Open src/Chat.js in your editor. You will see several TODO comments showing where you need to integrate with Chatkit. The first step is to wrap the Chat component in the withChatkitOneToOne higher-order component from the SDK. This will automatically inject the Chatkit SDK into the component props.

Import the withChatkitOneToOne higher-order component:

import Moment from 'react-moment';
import React, { useState, useEffect } from 'react';
import { withChatkitOneToOne } from '@pusher/chatkit-client-react';
 
import './Chat.css';
import defaultAvatar from './default-avatar.png';
 
function Chat(props) {
  const [pendingMessage, setPendingMessage] = useState('');
  const messageList = React.createRef();
 
  const handleMessageKeyDown = event => {
    if (event.key === 'Enter') {
      handleSendMessage();
    }
  };
 
/* Rest of file truncated */

Wrap the exported component:

/* Rest of file truncated */
              : 'Chat__messages__message Chat__messages__message--other'
          }
        >
          <div className="Chat__messages__message__content">{textContent}</div>
          <div className="Chat__messages__message__time">
            <Moment
              calendar={{
                sameDay: 'LT',
                lastDay: '[Yesterday at] LT',
                lastWeek: '[last] dddd [at] LT',
              }}
            >
              {createdAt}
            </Moment>
          </div>
          <div
            className={
              isOwnMessage
                ? 'Chat__messages__message__arrow alt'
                : 'Chat__messages__message__arrow'
            }
          />
        </div>
      </div>
    </div>
  );
}
// Last line in file
export default Chat;
export default withChatkitOneToOne(Chat);

Displaying user data

Now that your component is wrapped, you will get access to the Chatkit SDK in the component’s props under props.chatkit.

Display the other user’s name in the conversation title by accessing the props.chatkit.otherUser object:

/* Rest of file truncated */
 
return (
  <div className="Chat">
    <div className="Chat__titlebar">
      <img
        src={defaultAvatar
        className="Chat__titlebar__avatar"
        alt="avatar"
      />
      <div className="Chat__titlebar__details">
        {/*TODO: Get other user's name from Chatkit */}
        <span>[OTHER USERS NAME HERE]</span>
        <span>{props.chatkit.isLoading
                ? 'Loading...'
                : props.chatkit.otherUser.name}
        </span>
      </div>
    </div>
    <div className="Chat__messages" ref={messageList}>
      {messages.map(m => (
        <Message key={m.id} {...m} />
      ))}
    </div>
    <div className="Chat__compose">
      <input
        className="Chat__compose__input"
        type="text"
        placeholder="Type a message..."
        value={pendingMessage}
        onChange={handleMessageChange}
        onKeyDown={handleMessageKeyDown}
      />
      <button className="Chat__compose__button" onClick={handleSendMessage}>
        Send
      </button>
    </div>
  </div>
);
 
/* Rest of file truncated */

You should now see the other user’s name appear in the conversation title.

Displaying messages

Take the messages as given by the SDK and transform them in to the shape that we need to render them in this application. The code below is written to only work with simple messages – messages with exactly one text part. To learn more about displaying multi-part messages see the JavaScript reference docs.

/* Rest of file truncated */
const handleSendMessage = () => {
  if (pendingMessage === '') {
    return;
  }
  // TODO: Send message to Chatkit
  setPendingMessage('');
};
 
useEffect(() => {
  messageList.current.scrollTop = messageList.current.scrollHeight;
});
 
// TODO: Show messages from Chatkit
const messages = [
  {
    id: 0,
    isOwnMessage: false,
    createdAt: '01/01/2019',
    textContent: 'Hi there! This is hardcoded message.',
  },
  {
    id: 1,
    isOwnMessage: true,
    createdAt: '01/01/2019',
    textContent: 'Hey 👋, so is this.',
  },
];
const messages = props.chatkit.messages.map(m => ({
  id: m.id,
  isOwnMessage: m.sender.id === props.chatkit.currentUser.id,
  createdAt: m.createdAt,
  // This will only work with simple messages.
  // To learn more about displaying multi-part messages see
  // https://pusher.com/docs/chatkit/reference/javascript#messages
  textContent: m.parts[0].payload.content,
}));
 
return (
  <div className="Chat">
    <div className="Chat__titlebar">
/* Rest of file truncated */

Testing messages are shown correctly

Use the Instance Inspector to send a message to the new room. Click the Add message to room link and send a message from Bob.

Screenshot of the instance inspector showing the new room with Alice and Bob

You should see the message appear in your app 🥳. There won’t be any messages displayed until you send some! So don’t be alarmed by the empty space…

Screenshot of the example app showing a new message

Sending messages

The final step is to send messages from the app itself. Add a call to props.chatkit.sendSimpleMessage in the handleSendMessage function:

/* Rest of file truncated */
const handleMessageChange = event => {
  setPendingMessage(event.target.value);
};
 
const handleSendMessage = () => {
  if (pendingMessage === '') {
    return;
  }
  // TODO: Send message to Chatkit
  props.chatkit.sendSimpleMessage({ text: pendingMessage })
  setPendingMessage('');
};
 
useEffect(() => {
  messageList.current.scrollTop = messageList.current.scrollHeight;
});
/* Rest of file truncated */

Putting it all together

Open another tab and log in as Bob, verify that Alice can send messages to Bob and vice versa.

Conclusion

You have now successfully created a React application using Chatkit 🎉.

Next, you will want to learn how to secure your application and explore the rest of the React SDK

What else can I do with Chatkit?

For more detailed information on what else is possible, check out the rest of the docs: