Create a chat feature with a contact list by using TalkJS

In this tutorial we will create a direct message chat with a contact list using the TalkJS chat API. The chat will display a list of contacts from which a user can select a contact and open a chatbox with a conversation (and a history of that conversation if it exists).

This is similar to the functionality you see on platforms like Linkedin, WhatsApp and Telegram.

The code for this tutorial is available in the accompanying GitHub example.

Setting up the chat

The first thing we will do is set up a basic HTML page on which we will display our chat. On this page we will embed the TalkJS API in a script as specified in the Getting Started Guide.

We start by creating a div to hold our chatbox and the contact list, using the code below:

<div style="display: flex; justify-content: center">
    <!-- container element in which TalkJS will display a chat UI -->
    <div id="talkjs-container" style="width: 30%;>
        <i>Loading chat...</i>
    </div>

    <div id="contacts-list">
        <h2>Contacts</h2>
    </div>
</div>

Our div will have two divs within it with ids of talkjs-container and contact-list respectively. We also apply some basic styling so that the contacts-list div floats to the right side of the talkjs-container div.

The contact list

The contact-list div will be populated by the list of contacts we can chat with. In production it is likely that these users will be retrieved from a database or another location. In our case we will create an array of hardcoded users. The array will contain objects of the users, each with an id, name and photoUrl property.

So we add the following code to our script:

const contactsList = [
    {
        id: 1,
        name: 'Lisa',
        photoUrl: 'https://talkjs.com/images/avatar-1.jpg'
    },
    {
        id: 2,
        name: 'Alice',
        photoUrl: 'https://talkjs.com/images/avatar-2.jpg'
    },
    {
        id: 3,
        name: 'Tina',
        photoUrl: 'https://talkjs.com/images/avatar-3.jpg'
    },
    {
        id: 4,
        name: 'Lee',
        photoUrl: 'https://talkjs.com/images/avatar-4.jpg'
    },
    {
        id: 5,
        name: 'Pete',
        photoUrl: 'https://talkjs.com/images/avatar-5.jpg'
    },
    {
        id: 6,
        name: 'Dana',
        photoUrl: 'https://talkjs.com/images/avatar-6.jpg'
    },
    {
        id: 7,
        name: 'Ro',
        photoUrl: 'https://talkjs.com/images/avatar-7.jpg'
    },
];

After setting up up our array of users, the next step is to display them in the contacts-list div.

To display the contacts list we will loop through the contactsList array and use the contact objects inside that array to create a contacts list with usernames and profile pictures. We will also make the contacts clickable. Finally the list is rendered in the contacts-list div.

// Display contacts list on page
// Get contacts list container from the DOM
const contactsWrapper = document.getElementById('contacts-list');
// Loop through array and display each contact in contact-list div
for (let contact of contactsList) {
    // Extract contact details
    const id = contact.id;
    const username = contact.name;
    const photoUrl = contact.photoUrl;

    //create img tag to hold contact pic, give it a class name (for styling purposes) and add photo to it
    const contactPhoto = document.createElement('img');
    contactPhoto.classList.add('contact-photo');
    contactPhoto.src = photoUrl;

    // Create div to hold contact Name and add name
    const usernameDiv = document.createElement('div');
    usernameDiv.classList.add('contact-name');
    usernameDiv.innerText = username;

    // Create contact parent div and add to it contactPhotoDiv and usernameDiv
    const contactContainerDiv = document.createElement('div');
    contactContainerDiv.classList.add('contact-container');

    contactContainerDiv.appendChild(contactPhoto);
    contactContainerDiv.appendChild(usernameDiv);

    contactsWrapper.appendChild(contactContainerDiv);
};

Now that we have loaded our contact list, let us add some styling to make it look nicer.

Styling the contact list

Since we are going to be doing a bit of styling, we will open a style tag at the top of our page and put all our CSS code there.

We want to reduce the size of the profile pictures, make them rounded and also float the names to the right of the profile pictures. We will also add a hover pseudo-class so that the user can easily see which contact they can select, and change the cursor to a pointer when it hovers over a contact.

Let us add the following code to our page:

<style>

    #contacts-list {
        margin-top: auto;
        width: 700px;
        border: #d0d8dc solid 1px;
        border-radius: 6px;
        height: 510px;
        color: #111;
        font-family: 'Open Sans', sans-serif;
    }

    #contacts-list h2 {
        color: #111;
        background: #e7ecee;
        border-top-left-radius: 5px;
        border-top-right-radius: 5px;
        font-size: 14px;
        font-weight: 700;
        margin: 0;
        padding-top: 20px;
        padding-left: 20px;
        text-align: left;
        height: 40px;
        
    }
    
    #contact-container {
        height: 50px;
        display: flex;
        margin: 5px 0;
        padding: 5px 0;
        cursor: pointer; 
    }

    #contact-container:hover {
        background-color: #e7ecee;
    }

	.contact-name {
        display: flex;
        flex-direction: column;
        justify-content: space-around;
	}

    img {
        height: 40px;
        width: 40px;
        border-radius: 200px;
        margin-left: 20px;
        margin-right: 20px;
    }

</style>

The contact list will look as shown:

Loading the chatbox

After displaying the contact list we now want to initialize and load our chat interface.

We begin by asynchronously calling the Talk object. Once the promise is resolved, the rest of our code is loaded.

Talk.ready.then(function() {
    ...
})

Let us create a user called me who will be able to interact with the users from our array. As mentioned before, in production this user will likely be extracted from a database, or sent over a network in a data format such as the array above.

let me = new Talk.User({
    id: '0',
    name: 'Demi',
    photoUrl: 'https://talkjs.com/images/avatar.jpg'
});

We then use the appId to start and authenticate our chat Session with the TalkJS servers. This appId is found in the TalkJS dashboard, and without it the chat will not load.

Next we create and mount the chatbox where the conversation will be displayed:

const chatbox = talkSession.createChatbox();
chatbox.mount(document.getElementById('talkjs-container'));

We now want to create conversations between the user me and each of the users in the contactsList array. Here we make use of the Array.prototype.map() method which creates a new array populated with the results of calling a provided function on every element in the calling array.

// Create conversationBuilder objects for each user
const conversations = contactsList.map(function(user, index) {
    const talkUser = new Talk.User(user);

    conversation = talkSession.getOrCreateConversation(Talk.oneOnOneId(me, talkUser));

    conversation.setParticipant(me);
    conversation.setParticipant(talkUser);

    return conversation;
});

Connecting the chatbox to the contact list

The final step is to link our contacts to the appropriate chatbox, so that when a contact is selected a chatbox of that particular conversation loads.

To do this we need to get all instances of the contact-container class from the DOM, so we can listen in when any of the contacts is clicked. Then we add a click event listener to the resultant array and create a callback which is called when the event is triggered.

let contactsListDivs = document.getElementsByClassName('contact-container');
conversations.forEach(function(conversation, index) {
    contactsListDivs[index].addEventListener('click', function() {
    chatbox.select(conversation);
    });
});
}

Our final chat looks and functions as shown below:

Below is the full code for this tutorial.

<style>
    #contacts-list {
        margin-top: auto;
        width: 700px;
        border: #d0d8dc solid 1px;
        height: 510px;
		color: #111;
        font-family: 'Open Sans', sans-serif;
    }

    #contacts-list h2 {
        color: #111;
        background: #e7ecee;
        border-top-left-radius: 5px;
		border-top-right-radius: 5px;
		font-size: 14px;
		font-weight: 700;
		margin: 0;
        padding-top: 20px;
		padding-left: 20px;
        text-align: left;
        height: 40px;
    }

    .contact-container {
        height: 50px;
        display: flex;
        margin: 5px 0;
        padding: 5px 0;
        cursor: pointer; 
    }

    .contact-container:hover {
        background-color: #e7ecee;
    }

	.contact-name {
        display: flex;
        flex-direction: column;
        justify-content: space-around;
    }

    img {
        height: 40px;
        width: 40px;
        border-radius: 200px;
        margin-left: 20px;
        margin-right: 20px;
    }
</style>

<div style="display: flex; justify-content: center;">
    <!-- container element in which TalkJS will display a chat UI -->
    <div id="talkjs-container" style="width: 30%;">
    <i>Loading chat...</i>
    </div>

    <div id="contacts-list" style="width: 400px;">
        <h2>Contacts</h2>
    </div>

</div>

<script>
    const contactsList = [

        {
            id: 1,
            name: 'Lisa',
            photoUrl: 'https://talkjs.com/images/avatar-1.jpg'
        },
        {
            id: 2,
            name: 'Alice',
            photoUrl: 'https://talkjs.com/images/avatar-2.jpg'
        },
        {
            id: 3,
            name: 'Tina',
            photoUrl: 'https://talkjs.com/images/avatar-3.jpg'
        },
        {
            id: 4,
            name: 'Lee',
            photoUrl: 'https://talkjs.com/images/avatar-4.jpg'
        },
        {
            id: 5,
            name: 'Pete',
            photoUrl: 'https://talkjs.com/images/avatar-5.jpg'
        },
        {
            id: 6,
            name: 'Dana',
            photoUrl: 'https://talkjs.com/images/avatar-6.jpg'
        },
        {
            id: 7,
            name: 'Ro',
            photoUrl: 'https://talkjs.com/images/avatar-7.jpg'
        },
    ];
    
    // Display contacts list on page
    // Get contacts list container from the DOM
    const contactsWrapper = document.getElementById('contacts-list');
    // Loop through array and display each contact in contact-list div
    for (let contact of contactsList) {
        // Extract contact details
        const id = contact.id;
        const username = contact.name;
        const photoUrl = contact.photoUrl;

        //create img tag to hold contact pic, give it a class name (for styling purposes) and add photo to it
        const contactPhoto = document.createElement('img');
        contactPhoto.classList.add('contact-photo');
        contactPhoto.src = photoUrl;

        // Create div to hold contact Name and add name
        const usernameDiv = document.createElement('div');
        usernameDiv.classList.add('contact-name');
        usernameDiv.innerText = username;

        // Create contact parent div and add to it contactPhotoDiv and usernameDiv
        const contactContainerDiv = document.createElement('div');
        contactContainerDiv.classList.add('contact-container');

        contactContainerDiv.appendChild(contactPhoto);
        contactContainerDiv.appendChild(usernameDiv);

        contactsWrapper.appendChild(contactContainerDiv);
    };

    Talk.ready.then(function() {
    // Create user "me"
    let me = new Talk.User({
        id: '0',
        name: 'Josh',
        photoUrl: 'images/josh.webp'
    });

    // Start TalkJS Session
    window.talkSession = new Talk.Session({
        appId: 'tQWG4Gnl',
        me: me
    });

    // Create and mount the chatbox
    const chatbox = talkSession.createChatbox();
    chatbox.select(null);
    chatbox.mount(document.getElementById('talkjs-container'));

    // Create conversationBuilder objects for each user
    const conversations = contactsList.map(function(user, index) {
        const talkUser = new Talk.User(user);

        conversation = talkSession.getOrCreateConversation(Talk.oneOnOneId(me, talkUser));

        conversation.setParticipant(me);
        conversation.setParticipant(talkUser);

        return conversation;
    });

    // Listen for clicks on each contact and select the appropriate conversation
    let contactsListDivs = document.getElementsByClassName('contact-container');

    conversations.forEach(function(conversation, index) {
        contactsListDivs[index].addEventListener('click', () => {
        chatbox.select(conversation);
        });
    });
    });
</script>

Further customizations

It is possible to further customize your chat using the Theme Editor to make the UI look like WhatsApp, or Discord or Youtube. Check out more TalkJS tutorials.

Happy coding!