Add an animated typing indicator to your React chat app

Posted on November 11, 2019


A typing indicator is a small interface component used to notify the user that the person on the other side is currently typing a message into the text box.

It creates effective communication between your user because if your user is aware that the other person is preparing a message, he or she will likely wait before typing a reply and stay on your app longer.

No modern chat application is complete without a typing indicator, and in this tutorial you will learn how to implement one using React library and CometChat. Here is the final result:

Here’s the complete code on GitHub if you’d like to jump straight into the code, but feel free to follow along if you’re curious about how to create one.

Preparing CometChat

First thing first, we’re going to use CometChat to power our chat application.

In case if you’re not familiar with it, CometChat is a developer tool that enables you to add text, voice, and video chat to your web and mobile applications with ease. Integrating its SDK will save you lots of development hours.

Best of all, it has a free version, so you can register a new account on its website and start enabling its services for your React app.

Create a new CometChat application by clicking on the the + icon:

Add a new CometChat app

Once done, you’ll be taken into the dashboard of your app, preloaded with default users and API key that you can immediately use. Go ahead and click on API Keys. From here, take note of your API key and App ID because you’re going to use it in your app later:

CometChat API Key and App ID

And you’re done setting up CometChat. Next up is creating your chat app.

Creating a simple chat application

Now you’re ready to start coding the application. Since we’re going to focus only on creating the typing indicator, you can clone this starter code I have prepared for this tutorial.

This code contains a basic React chat app powered by CometChat already, so you don’t have to code anything before creating the typing indicator. If you’re interested in building the components from zero yourself, you can follow my other tutorial here.

Let’s start downloading the required modules by using the command line npm install.

After your installation is finished, rename .env-example file to .env and edit its content with your CometChat credentials:

REACT_APP_COMETCHAT_API_KEY=YOUR_API_KEY
REACT_APP_COMETCHAT_APP_ID=YOUR_APP_ID
REACT_APP_COMETCHAT_GUID=supergroup

Copy and paste your CometChat Api Key to REACT_APP_COMETCHAT_API_KEY and your App ID to REACT_APP_COMETCHAT_APP_ID. You will reference them from the app later.

You can run npm start and test your React app. A basic JavaScript prompt will appear asking you for input. You can use sample users like superhero1 or superhero2 to login.

s FBE4382ECB88CAB2D0DC3C6C18985A475E3C8299CAD592844D134AEEACDCFE36 1572454049219 login

Once logged in, you will see the chat window and a textbox to send messages. If this is your first chat app, the chat window should be empty. Why don’t you try to send a message?

s FBE4382ECB88CAB2D0DC3C6C18985A475E3C8299CAD592844D134AEEACDCFE36 1572670037606 2019 11 02 11 46 32 Window

Now that our chat app is running, let’s start implementing the animated typing indicator.

How typing indicator works

According to CometChat documentation, we can call the startTyping() function in order to send a signal into the receiving end of the app. To receive that signal, we need to implement the onTypingStarted function, which is a listener function that can be registered to CometChat``.``MessageListener class.

It’s very easy to do this in our React app. Let me show you how.

Let’s start by creating a function that calls on the startTyping function. This function is executed anytime a user is typing a message. Let’s call it the typingListener function:

typingListener = () => {
  const receiverId = REACT_APP_COMETCHAT_GUID;
  const receiverType = CometChat.RECEIVER_TYPE.GROUP;
  const typingNotification = new CometChat.TypingIndicator(
    receiverId,
    receiverType
  );
  console.log('Sending typing notification to listener');
  CometChat.startTyping(typingNotification);
}

Now, this function needs to run every time a user type into our textbox. To do that, We only need to pass the function into the ChatBox component. It will take care of the rest:

<ChatBox 
  // other props..
  typingListener={this.typingListener}
/>

Receiving typing indicators

To receive the incoming typing signal that is being sent into the group chat, we need to extend our listener with the typing events and store typing users data into the component state.

First, Add a new state property currentlyTyping to store users that are currently typing:

this.state = {
  // other states..
  currentlyTyping: [],
};

Next, extend our listener to receive incoming typing events. We’ll implement onTypingStarted and onTypingEnded events to the listener object:

CometChat.addMessageListener(
  'CC-LISTENER-ID',
  new CometChat.MessageListener({
    onTextMessageReceived: message => {
      const {messages} = this.state;
      console.log('Incoming Message Log', {message});
      messages.push(message);
      this.setState({
        messages,
      });
    },
    onTypingStarted: typingIndicator => {
      console.log('Typing started :', typingIndicator);
      const {currentlyTyping} = this.state;
      const {sender} = typingIndicator;
      if (currentlyTyping.length > 0) {
        if (!currentlyTyping.some(element => element.uid === sender.uid)) {
          currentlyTyping.push(sender);
        }
      } else {
        currentlyTyping.push(sender);
      }
      this.setState({
        currentlyTyping,
      });
    },
    onTypingEnded: typingIndicator => {
      console.log('Typing ended :', typingIndicator);
      const {currentlyTyping} = this.state;
      const {sender} = typingIndicator;
      const newCurrentlyTyping = currentlyTyping.filter(
        element => element.uid !== sender.uid
      );
      this.setState({
        currentlyTyping: newCurrentlyTyping,
      });
    },
  })
);

Let me explain what we’re doing with the typing events above.

When a new typingIndicator object is received by onTypingStarted function, we’ll check on the value of currentlyTyping array. If the sender’s uid is not present inside the array element, we’ll add the sender object into the array and set it to state.

On the other hand, when onTypingEnded receive a typingIndicator object, we’ll filter out the sender object from the currentlyTyping array and set the new array to state.

The point is these two listeners will work together to update the currentlyTyping state value.

Now you might wonder as we’re not implementing the endTyping() function, how can we trigger the onTypingEnded listener?

No worries! We don’t have to explicitly call on endTyping() function because CometChat SDK is sophisticated enough to realize when the sender hasn’t been typing for a while since the last startTyping function is called, and will automatically call on endTyping function for the chat room you’re app currently connected to.

Now that we have a working “typing users” state in our component, let’s use it to show some typing indicator in our chat window.

Writing the indicator

Let’s use the currentlyTyping state to create a string that will inform our users about who is currently typing a message. We’ll set a multiple if conditions because we want to display the name of users who are currently typing when there are 2 or fewer users in the state value. But when there are more than 2 users, we’ll simply output “Several people are typing”.

Once the typingText value is assigned, we will create a new variable containing JSX elemet to render the value. Let’s name it typingIndicator:

const {messages, isLoading, user, currentlyTyping} = this.state;

let typingText = '';
if (currentlyTyping.length === 1) {
  typingText = `${currentlyTyping[0].name} is typing `;
} else if (currentlyTyping.length === 2) {
  typingText = `${currentlyTyping[0].name} and ${currentlyTyping[1].name} are typing `;
} else if (currentlyTyping.length > 2) {
  typingText = `Several people are typing `;
} else {
  typingText = '';
}

const typingIndicator = (
  <div id='typing-indicator'>
    {typingText}
  </div>
);

All we have to do is pass the typingIndicator constant into the ChatBox component:

<ChatBox 
  // other props..
  typingIndicator={typingIndicator}
/>

Open your chat app in two different browsers, and you can view the typing indicator is shown when you start typing into the textbox. Yet here you’ll notice that the indicator seems a bit disorganized, so let’s add a bit of CSS to make it better:

#typing-indicator {
  padding-bottom: 30px;
  padding-top: 30px;
  padding-left: 20px;
}

Now it flows with the layout of the chat window. Great!

With our typing indicator implemented, we only need to add a small animated dots to make it look even more pretty. We’re going to use this lovely animation created by Jimmy Yoon. All we need to do is the CSS code into our App.css file:

.typing-dot {
  height: 5px;
  width: 5px;
  border-radius: 100%;
  margin-left: 4px;
  display: inline-block;
  background-color: #5C5E69;
  animation: 1.2s typing-dot ease-in-out infinite;
}
.typing-dot:nth-of-type(2) {
  animation-delay: 0.15s;
}
.typing-dot:nth-of-type(3) {
  animation-delay: 0.25s;
}
@keyframes typing-dot {
  15% {
    transform: translateY(-35%);
    opacity: 0.5;
  }
  30% {
    transform: translateY(0%);
    opacity: 1;
  }
}

Then we’ll create a variable to store the indicator as JSX element. When the app creates the typingIndicator variable, We’ll check on the value of typingText, and when it’s not empty, display the typingAnimation :

const typingAnimation = (
  <React.Fragment>
    <span className='typing-dot'></span>
    <span className='typing-dot'></span>
    <span className='typing-dot'></span>
  </React.Fragment>
);

const typingIndicator = (
  <div id='typing-indicator'>
    {typingText} {typingText ? typingAnimation : ''}
  </div>
);

Hit the chat app again. This time our typing indicator will show three animated dots. Awesome!

Conclusion

You have learned how to implement a typing indicator using CometChat and React. Now your users can communicate better because they know that the person they’re chatting with is currently typing a message or not.

Congratulations on making it to the finish line! Don’t forget to check out other in depth tutorials about creating modern chat application from our team.

React Distilled 2.0 is released

If you'd like to learn more about React and how you can use it to build a complete web application from scratch, I'm offering a 28% off my book React Distilled to celebrate its release (from $49 to $34). 

It includes new chapters on React Context API and React Hooks, and it shows how you can create React app using nothing but React and Firestore.

Share this:
LinkedIn
Reddit
WhatsApp

Get Free Guides