Add emoji support to a React chatroom

Add emoji support to a React chatroom

In this tutorial, we’ll add emoji support to a chatroom built using React and Chatkit. We’ll explore two common ways of using emojis: through an emoji picker, and using the : shortcut to quickly search for and select and emoji.

In this tutorial, we’ll add emoji support to a chatroom built using React and Chatkit. We’ll explore two common ways of using emojis: through an emoji picker, and using the : shortcut to quickly search for and select and emoji.

Here’s a live demo of what we’ll be building:

This article builds upon what was covered in the tutorial on sending direct messages with Chatkit, so you need to go over that one first before moving on to this one. You can clone this GitHub repository and follow the instructions in the README file to get set up.

Prerequisites

You need to have Node.js (version 8 or later) installed on your machine to be able to follow through with this tutorial. You can check out this page for instructions on how to upgrade your Node installation.

Install additional dependencies

We need a few additional dependencies to build this project. react-textarea-autocomplete provides the ability to autocomplete emojis in the text input, when emoji-mart provides a beautiful emoji picker that we can take advantage of. It looks and works almost exactly like the one in Slack in case you’re wondering. Finally, we’re adding the react-feather package for icons.

Install them all by navigating to the client directory, and running the command below:

    npm install @webscopeio/react-textarea-autocomplete react-feather emoji-mart --save

Update the styles for the application

Go ahead and update the styles for the app in App.css as shown below. This is to account for the new features and components that have been added to the application.

    // client/src/App.css

    html {
      box-sizing: border-box;
    }

    *, *::before, *::after {
      box-sizing: inherit;
      margin: 0;
      padding: 0;
    }

    ul {
      list-style: none;
    }

    h4 {
      padding-left: 20px;
      margin-bottom: 10px;
    }

    .App {
      width: 100vw;
      height: 100vh;
      display: grid;
      grid-template-columns: 1fr 4fr 1fr;
    }

    .right-sidebar {
      border-left: 1px solid #ccc;
    }

    .left-sidebar {
      border-right: 1px solid #ccc;
    }

    .user-profile {
      height: 70px;
      display: flex;
      align-items: flex-start;
      padding-right: 20px;
      padding-left: 20px;
      justify-content: center;
      flex-direction: column;
      border-bottom: 1px solid #ccc;
    }

    .user-profile span {
      display: block;
    }

    .user-profile .username {
      font-size: 20px;
      font-weight: 700;
    }

    .chat-rooms li, .room-member {
      display: flex;
      align-items: center;
      padding: 15px 20px;
      font-size: 18px;
      color: #181919;
      cursor: pointer;
      border-bottom: 1px solid #eee;
      margin-bottom: 0;
    }

    .room-member {
      justify-content: space-between;
      padding: 0 20px;
      height: 60px;
    }

    .send-dm {
      opacity: 0;
      pointer-events: none;
      font-size: 20px;
      border: 1px solid #eee;
      border-radius: 5px;
      margin-bottom: 0;
      padding: 0 10px;
      line-height: 1.4;
      height: auto;
    }

    .room-member:hover .send-dm {
      opacity: 1;
      pointer-events: all;
    }

    .presence {
      display: inline-block;
      width: 10px;
      height: 10px;
      background-color: #ccc;
      margin-right: 10px;
      border-radius: 50%;
    }

    .presence.online {
      background-color: green;
    }

    .chat-rooms .active {
      background-color: #eee;
      color: #181919;
    }

    .chat-rooms li:hover {
      background-color: #D8D1D1;
    }

    .room-icon {
      display: inline-block;
      margin-right: 10px;
    }

    .chat-screen {
      display: flex;
      flex-direction: column;
      height: 100vh;
    }

    .chat-header {
      height: 70px;
      flex-shrink: 0;
      border-bottom: 1px solid #ccc;
      padding-left: 10px;
      padding-right: 20px;
      display: flex;
      flex-direction: column;
      justify-content: center;
    }

    .chat-header h3 {
      margin-bottom: 0;
      text-align: center;
    }

    .chat-messages {
      flex-grow: 1;
      overflow-y: auto;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      margin-bottom: 0;
      min-height: min-content;
      position: relative;
    }

    .message {
      padding-left: 20px;
      padding-right: 20px;
      margin-bottom: 10px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    .message span {
      display: block;
      text-align: left;
    }

    .message .user-id {
      font-weight: bold;
    }

    .message-form {
      border-top: 1px solid #ccc;
      width: 100%;
      display: flex;
      align-items: center;
    }

    .message-form, .message-input {
      width: 100%;
      margin-bottom: 0;
    }

    .rta {
      flex-grow: 1;
    }

    .emoji-mart {
      position: absolute;
      bottom: 20px;
      right: 10px;
    }

    input[type="text"].message-input, textarea.message-input {
      height: 50px;
      flex-grow: 1;
      line-height: 35px;
      padding-left: 20px;
      border-radius: 0;
      border-top-left-radius: 0;
      border-top-right-radius: 0;
      border-bottom-left-radius: 0;
      border-bottom-right-radius: 0;
      border: none;
      font-size: 16px;
      color: #333;
      min-height: auto;
      overflow-y: hidden;
      resize: none;
      border-left: 1px solid #ccc;
    }

    .message-input:focus {
      outline: none;
    }

    .toggle-emoji {
      border: none;
      width: 50px;
      height: auto;
      padding: 0;
      margin-bottom: 0;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .toggle-emoji svg {
      width: 28px;
      height: 28px;
    }

    /* RTA
       ========================================================================== */

    .rta {
      position: relative;
      border-left: 1px solid #ccc;
      display: flex;
      flex-direction: column;
    }

    .rta__autocomplete {
      position: absolute;
      width: 300px;
      background-color: white;
      border: 1px solid #ccc;
      border-radius: 5px;
    }

    .rta__autocomplete ul {
      list-style: none;
      text-align: left;
      margin-bottom: 0;
    }

    .rta__autocomplete li {
      margin-bottom: 5px;
      padding: 3px 20px;
      cursor: pointer;
    }

    .rta__autocomplete li:hover {
      background-color: skyblue;
    }

    /* Dialog
       ========================================================================== */

    .dialog-container {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.8);
      display: flex;
      justify-content:center;
      align-items: center;
    }

    .dialog {
      width: 500px;
      background-color: white;
      display: flex;
      align-items:  center;
    }

    .dialog-form {
      width: 100%;
      margin-bottom: 0;
      padding: 20px;
    }

    .dialog-form > * {
      display: block;
    }

    .username-label {
      text-align: left;
      font-size: 16px;
    }

    .username-input {
      width: 100%;
    }

    input[type="text"]:focus {
      border-color: #5C8436;
    }

    .submit-btn {
      color: #5C8436;
      background-color: #181919;
      width: 100%;
    }

    .submit-btn:hover {
      color: #5C8436;
      background-color: #222;
    }

Add the emoji picker component

In any sort of non-trivial chat application, an emoji picker is an ubiquitous feature. So we’re going to start by adding the ability to input emojis into text using the emoji picker provided by emoji-mart.

First, let’s add a button that can be used to toggle the emoji picker. Open up App.js and change it to look like this:

    // client/src/App.js

    import React, { Component } from 'react';
    import { Smile } from 'react-feather';

    // [..]

    class App extends Component {
      constructor() {
        super();
        this.state = {
          // [..]
          showEmojiPicker: false,
        };

        // [..]
      }

      render() {
        const {
          // [..]
          showEmojiPicker,
        } = this.state;

        return (
          <div className="App">
            // [..]
            <section className="chat-screen">
              // [..]
              <footer className="chat-footer">
                <form onSubmit={this.sendMessage} className="message-form">
                  <button
                    type="button"
                    className="toggle-emoji"
                  >
                    <Smile />
                  </button>
                  <input
                    type="text"
                    value={newMessage}
                    name="newMessage"
                    className="message-input"
                    placeholder="Type your message and hit ENTER to send"
                    onChange={this.handleInput}
                  />
                </form>
              </footer>
            </section>
            // [..]
          </div>
        );
      }
    }

    export default App;

The app should look like this now:

Next, let’s make the button functional by toggling the emoji picker component from emoji-mart on click. Update the code in App.js to look like this:

    // client/src/App.js

    import React, { Component } from 'react';
    import { Smile } from 'react-feather';
    import { Picker } from 'emoji-mart';

    import {
      // [..]
      addEmoji,
      toggleEmojiPicker,
    } from './methods';
    // [..]

    import 'emoji-mart/css/emoji-mart.css';
    // [..]

    class App extends Component {
      constructor() {
        // [..]

        this.addEmoji = addEmoji.bind(this);
        this.toggleEmojiPicker = toggleEmojiPicker.bind(this);
      }

      render() {
        const {
          // [..]
        } = this.state;

        return (
          <div className="App">
            // [..]
            <section className="chat-screen">
              // [..]
              <ul className="chat-messages">
                <ChatSession messages={messages} />
                {showEmojiPicker ? (
                  <Picker set="emojione" onSelect={this.addEmoji} />
                ) : null}
              </ul>
              <footer className="chat-footer">
                <form onSubmit={this.sendMessage} className="message-form">
                  <button
                    type="button"
                    className="toggle-emoji"
                    onClick={this.toggleEmojiPicker}
                  >
                    <Smile />
                  </button>
                  <input
                    type="text"
                    value={newMessage}
                    name="newMessage"
                    className="message-input"
                    placeholder="Type your message and hit ENTER to send"
                    onChange={this.handleInput}
                  />
                </form>
              </footer>
            </section>
            // [..]
          </div>
        );
      }
    }

    export default App;

Then update the methods.js file with the following new functions:

    // client/src/methods.js
    import Chatkit from '@pusher/chatkit-client';
    import axios from 'axios';

    function toggleEmojiPicker() {
      this.setState({
        showEmojiPicker: !this.state.showEmojiPicker,
      });
    }

    function addEmoji(emoji) {
      const { newMessage } = this.state;
      const text = `${newMessage}${emoji.native}`;

      this.setState({
        newMessage: text,
        showEmojiPicker: false,
      });
    }

    // [..]

    export { sendMessage, handleInput, connectToRoom, connectToChatkit, sendDM, toggleEmojiPicker, addEmoji };

The toggleEmojiPicker() method shows and hides the emoji picker component, while addEmoji() updates the message input with the selected emoji. You can try it out by selecting an emoji from the emoji picker. It should work as shown below:

Autocomplete emoji in the text input

An added way to quickly input emoji in a message is by triggering autocomplete by typing :, followed by the first few letters and a menu will appear where you can select the emoji you want to use. We’ll implement this functionality in our application using react-textarea-autocomplete. Make the following changes in App.js:

    // client/src/App.js

    import React, { Component } from 'react';
    import { Picker, emojiIndex } from 'emoji-mart';
    import { Smile } from 'react-feather';
    import ReactTextareaAutocomplete from '@webscopeio/react-textarea-autocomplete';

    import {
      // [..]
      handleKeyPress,
    } from './methods';

    // [..]

    class App extends Component {
      constructor() {
        // [..]

        this.handleKeyPress = handleKeyPress.bind(this);
      }

      render() {
        // [..]

        return (
          <div className="App">
            // [..]
            <section className="chat-screen">
              // [..]
              <footer className="chat-footer">
                <form onSubmit={this.sendMessage} className="message-form">
                  <button
                    type="button"
                    className="toggle-emoji"
                    onClick={this.toggleEmojiPicker}
                  >
                    <Smile />
                  </button>
                  <ReactTextareaAutocomplete
                    className="message-input my-textarea"
                    name="newMessage"
                    value={newMessage}
                    loadingComponent={() => <span>Loading</span>}
                    onKeyPress={this.handleKeyPress}
                    onChange={this.handleInput}
                    placeholder="Compose your message and hit ENTER to send"
                    trigger={{
                      ':': {
                        dataProvider: token =>
                          emojiIndex.search(token).map(o => ({
                            colons: o.colons,
                            native: o.native,
                          })),
                        component: ({ entity: { native, colons } }) => (
                          <div>{`${colons} ${native}`}</div>
                        ),
                        output: item => `${item.native}`,
                      },
                    }}
                  />
                </form>
              </footer>
            </section>
            // [..]
          </div>
        );
      }
    }

    export default App;

Then, update methods.js with the new handleKeyPress() method and update the sendMessage() method:

    // client/src/methods.js

    import Chatkit from '@pusher/chatkit-client';
    import axios from 'axios';

    function handleKeyPress(event) {
      if (event.key === 'Enter') {
        event.preventDefault();
        this.sendMessage();
      }
    }

    // [..]

    function sendMessage() {
      const { newMessage, currentUser, currentRoom } = this.state;

      if (newMessage.trim() === '') return;

      currentUser.sendMessage({
        text: newMessage,
        roomId: `${currentRoom.id}`,
      });

      this.setState({
        newMessage: '',
      });
    }

    export { sendMessage, handleInput, connectToRoom, connectToChatkit, sendDM, toggleEmojiPicker, addEmoji, handleKeyPress };

In ReactTextareaAutocomplete, the dataProvider is called after each keystroke to get an array of what the suggestion list should display, and component is the component for rendering the item in suggestion list. Finally, the output defines text which will be placed into text area after the user makes a selection.

Try it out by typing :, followed by the first few letters of the emoji you want to input. It should display a few suggestions and you can click on anyone of them to make your selection.

Wrap up

In this tutorial, we learned how to jazz up your React-based chatroom with emoji support. We covered how to use add an emoji picker component, and also how to trigger emoji suggestions using the : shortcut.

You can checkout other things Chatkit can do by viewing its extensive documentation. Don’t forget to grab the full source code used in this tutorial in this GitHub repository.

reactjs

What's new in Bootstrap 5 and when Bootstrap 5 release date?

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Random Password Generator Online

HTML Color Picker online | HEX Color Picker | RGB Color Picker

Why ReactJS is better for Web Application Development?

Web Application Development is the point of contact for a business in today's digital era. It is important to choose the right platform for Web Application Development to build a high end Web

ReactJs Course | How to build a YouTube Clone using ReactJS

Hello, this is a new tutorial series about ReactJs. In this series, I will teach you two important topics. First, I will be teaching you what is ReactJs library and why this is so powerful and expandable. We will see what are the important...

Top Reasons to Choose ReactJS for Front-End Development - Prismetric

Why ReactJS is perfect choice for your next web application? Get to know all about ReactJS development and its benefits in detail for frontend development.

ReactJS Development Services | ReactJS Development Company - Chapter 247 Infotech

Chapter 247 Infotech is a leading ReactJS development company in India, USA, offering ReactJS development services at par to a spectrum of business domains from E-commerce, healthcare to Edutech at

Pagination in ReactJs

There are a lot of resourceful materials online that give good insights into pagination in ReactJs, as well as NPM packages you can easily use