Build a real-time chat app with Vuejs, socket.IO, and Nodejs

by Solomon Eseme

.

Updated Sun Jun 18 2023

Build a real-time chat app with Vuejs, socket.IO, and Nodejs

Following my previous tutorials on Vue.js for backend developers where I listed out important things a backend developer needs to learn in Vue.js, so I decided to make use of some of the listed items to build a real-time chat app with Vuejs, socket.io, and Nodejs.

In this article, I will be showing you how I build a real-time chat app with VUEJS, NODEJS, EXPRESS, and SOCKET.IO.

Here is a screenshot of what we’ll build:

chatappvuedemo.jpg

N/B: Am not a front end guy, so don’t laugh at my design yet, colors and designs are my enemies l am walking hard to befriend them. This is just to demonstrate though.

I have created an updated version of Build a Real-time Chat App with Vue 3, Socket.io and Nodejs using Vue 3 and added more features such as authentication, authorization, and database functionalities.

Before you dive in, if you’re a backend developer or looking at delving into this career path, join other developers to receive daily articles on backend development that will boost your productivity.

Build A Real-time Chat App

SETUP

Node.js and NPM are required in this tutorial, if you don’t have NodeJS installed already you can install from here.

  • Basic knowledge of JavaScript is required.

  • Also, little or no knowledge of VUEJS is required.

If everything is all set up, let’s get started.

Create a directory for the application, open the directory with your favorite editor, I’m using visual studio code.

You can use the terminal, if you wish.

mkdir ChatApp && cd ChatApp && code .

Next, let’s initialize the project with NPM.

npm init

When prompted to fill in some information, go ahead or press enter for default options. The information will be used to set up your package.json file.

DEPENDENCIES INSTALLATION.

Let’s install our application dependencies. I will drop a list of needed dependencies and how to install them. We will keep it simple and only install these two dependencies.

i. EXPRESS (Read about it here)

npm install express --save

ii. SOCKET.IO (Read about it here)

npm install --save socket.io

After installing all the dependencies, run

npm install

It’ll install all the required packages.

FRONT-END WITH VUEJS (MARKUP)

In the front-end, we will be using Vue.js (Read about it ), let’s move to install Vue on our directory and also bootstrap 4.3.1 (Read about it here)

Create an index.html file.

touch index.html

To include Vue.js and bootstrap in our project, we’ll just copy the CDN (Content Delivery Network) and include it in the script section of our index.html file.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>ChatApp_Socket</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous" />
        
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
    <script src="/socket.io/socket.io.js"></script>
</head>

After installing Vue and bootstrap successfully, let’s move down to creating the markups with Vue and bootstrap.

<body>
    <div id="app">
        <div class="container">
            <div class="col-lg-6 offset-lg-3">

                <div v-if="ready">
                    <p v-for="user in info">
                        {{user.username}} {{user.type}}
                    </p>
                </div>

                <div v-if="!ready">
                    <h4>Enter your username</h4>
                    <form @submit.prevent="addUser">
                        <div class="form-group row">
                            <input type="text" class="form-control col-9" v-model="username"
                                placeholder="Enter username here">
                            <input type="submit" value="Join" class="btn btn-sm btn-info ml-1">

                        </div>
                    </form>
                </div>
                <h2 v-else>{{username}}</h2>
                <div class="card bg-info" v-if="ready">
                    <div class="card-header text-white">
                        <h4>My Chat App <span class="float-right">{{connections}} connections</span></h4>
                    </div>
                    <ul class="list-group list-group-flush text-right">
                        <small v-if="typing" class="text-white">{{typing}} is typing</small>
                        <li class="list-group-item" v-for="message in messages">
                            <span :class="{'float-left':message.type === 1}">
                                {{message.message}}
                                <small>:{{message.user}}</small>
                            </span>
                        </li>
                    </ul>

                    <div class="card-body">
                        <form @submit.prevent="send">
                            <div class="form-group">
                                <input type="text" class="form-control" v-model="newMessage"
                                    placeholder="Enter message here">
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>

</body>

To connect Socket.IO server to the client, we’ll add the Socket.IO client-side JavaScript library, which will help us gain connection between the server and the client.

<script src="/socket.io/socket.io.js"></script>

That will be our Vue and bootstrap (HTML) file for the front-end. You can grab the entire code for the front-end here to follow along.

The best way to learn is to follow along

You can download the client-side Socket.IO library here as well.

It’s up to you to do separation of concerns by separating the JavaScript codes from the markups, but in my case, I will include them together for easy access.

Take a break and subscribe to receive daily information that will boost your productivity as a Nodejs Backend Developer.

SETTING UP OUR SERVER.

Create a server.js, inside the server.js file, let’s create and configure the express server to work with Socket.IO.

let app = require('express')();
let http = require('http').Server(app);
let io = require('socket.io')(http);

app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html')
});


http.listen(3000, () => {
    console.log('Listening on port *: 3000');
});

This is the basic configuration required to set up Socket. IO in the backend.

HOW SOCKET.IO WORKS

Socket.IO is a JavaScript real-time chat library, you can read the documentation here since it’s outside the scope of this article, but I will try to explain a little that will be useful for this article.

Socket.IO works by adding event listeners (Read about it here) to an instance of http.Server which is what we are doing here.

let app = require('express')();
let http = require('http').Server(app);
let io = require('socket.io')(http);

app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html')
});


http.listen(3000, () => {
    console.log('Listening on port *: 3000');
});

Next, we will listen for a new connection event.

io.on('connection', (socket) => {

    socket.on('disconnect', () => {
        console.log("A user disconnected");
    });
    
   .....
    
    });

For example, if a new user visits 127.0.0.1:3000, the message in the Console.log will be printed to the console.

The Socket.IO has many flags or methods we can call to perform many functions such as emitting an event, listening to events, etc., which we are going to make use of this article.

Socket.ON(): Takes an event name and a callback as parameters, it listens for an event emitted from the server or vice versa, and the callback is used to retrieve any data associated with the event.

Socket.EMIT(): This emits/sends an event with or without data to the listening sockets including itself.

Socket.Broadcast.Emit(): This emits an event to other connected clients without itself included. We will be using these methods to send different events from the server throughout the Chat App.

SETTING UP OUR CLIENT-SIDE CODE

Open up your index.html at the bottom part add the following code inside the script tag.

<script>
    var socket = io();
    let vue = new Vue({
        el: '#app',

        data: {
            newMessage: null,
            messages: [],
            typing: false,
            username: null,
            ready: false,
            info: [],
            connections: 0,
        },

        created() {
            window.onbeforeunload = () => {
                socket.emit('leave', this.username);
            }
            
            socket.on('chat-message', (data) => {
                this.messages.push({
                    message: data.message,
                    type: 1,
                    user: data.user,
                });
            });

            socket.on('typing', (data) => {
                this.typing = data;
            });


            socket.on('stopTyping', () => {
                this.typing = false;
            });

            socket.on('joined', (data) => {
                this.info.push({
                    username: data,
                    type: 'joined'
                });

                setTimeout(() => {
                    this.info = [];
                }, 5000);
            });

            socket.on('leave', (data) => {
                this.info.push({
                    username: data,
                    type: 'left'
                });

                setTimeout(() => {
                    this.info = [];
                }, 5000);
            });

            socket.on('connections', (data) => {
                this.connections = data;
            });
        },

        watch: {
            newMessage(value) {
                value ? socket.emit('typing', this.username) : socket.emit('stopTyping')
            }
        },

        methods: {
            send() {
                this.messages.push({
                    message: this.newMessage,
                    type: 0,
                    user: 'Me',
                });

                socket.emit('chat-message', {
                    message: this.newMessage,
                    user: this.username
                });
                this.newMessage = null;
            },

            addUser() {
                this.ready = true;
                socket.emit('joined', this.username)
            }
        },

    });
</script>

</html>

We added the var socket=io(); and created a new Vue instance, also inside the newly created Vue instance, we set up our element with el: ‘#app’ and also declared our data object with some empty arrays and properties.

Let’s move down to the methods object, we can see a Send() method which stores our chat details into the message array and uses Socket.emit() flag to send a chat event to the server.

methods: {
            send() {
                this.messages.push({
                    message: this.newMessage,
                    type: 0,
                    user: 'Me',
                });

                socket.emit('chat-message', {
                    message: this.newMessage,
                    user: this.username
                });
                this.newMessage = null;
            },
              
              .....

At the server side, we receive the event with the Socket.on() flag and resend it back to other connected clients using theSocket.broadcast.emit() flag.

On the vue CREATED hook we listen to all the events emitted by the server, including the chat-message event we resent from the server earlier.

By broadcasting it, that means the server will send it to every other person connected to the server apart from the sender.

Analogy

Let’s have a clear picture of what “build a real-time chat app with Vuejs, socket.io, and Nodejs” really means.

So, if Mr. A sends the message to the server and the server re-broadcast it, Mr. B, C, D, E, etc. will receive the message but Mr. A won’t because it’s the sender.

So, don’t worry even if we listen to the chat-message event on the CREATED object, we won’t receive our own message back from the server.

This is what we will do throughout the different user’s actions on the ChatApp.

With that out of the way, after receiving the event from the from the server in the CREATED Object, we will store it in our message array with message type (indicating it’s a message from the server) and the user (who sent the message).

           socket.on('chat-message', (data) => {
                this.messages.push({
                    message: data.message,
                    type: 1,
                    user: data.user,
                });
            });

To summarize everything happening in the front-end part with each cue. Jews object, I will list and explain what is happening in each of them.

Methods Hook:

In Vue.js, this is where all the methods/functions in your component are created, each time you call a method in the markup or template, Vue will look for it there.

So far, we have only two methods declared viz:

i. Send(): When a user types a message and click on the send button, this method is called to store the message in the message array with other detail and also emit an event to the server which I explain the process above.

ii. addUser(): The method emits the ‘joined’ event to the server and sets the ‘ready’ variable to ‘true’ showing the user has successfully joined the chat room.

            addUser() {
                this.ready = true;
                socket.emit('joined', this.username)
            }

Created Hook:

In Vue.js, the created hook is called when the Vue.js component has loaded. So, this a good place to listen to all our events from the server.

We are listening to 6 events from the server and emitting 1 event to the server.

created() {
            window.onbeforeunload = () => {
                socket.emit('leave', this.username);
            }
            
            socket.on('chat-message', (data) => {
                this.messages.push({
                    message: data.message,
                    type: 1,
                    user: data.user,
                });
            });

            socket.on('typing', (data) => {
                this.typing = data;
            });


            socket.on('stopTyping', () => {
                this.typing = false;
            });

            socket.on('joined', (data) => {
                this.info.push({
                    username: data,
                    type: 'joined'
                });

                setTimeout(() => {
                    this.info = [];
                }, 5000);
            });

            socket.on('leave', (data) => {
                this.info.push({
                    username: data,
                    type: 'left'
                });

                setTimeout(() => {
                    this.info = [];
                }, 5000);
            });

            socket.on('connections', (data) => {
                this.connections = data;
            });
        },

From the code above, we should understand what each of them does, if not, use the comment section for questions.

Watch hook:

The watch hook is a very interesting one, it can do a lot, but we used it to listen to changes on the message box and emit a typing event which is being broadcast back to other clients by the server.

        watch: {
            newMessage(value) {
                value ? socket.emit('typing', this.username) : socket.emit('stopTyping')
            }
        },

And in the markups with used vue.js directives to markup our view. Take a look at the full index.html file.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>ChatApp_Socket</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous" />
</head>

<body>
    <div id="app">
        <div class="container">
            <div class="col-lg-6 offset-lg-3">

                <div v-if="ready">
                    <p v-for="user in info">
                        {{user.username}} {{user.type}}
                    </p>
                </div>

                <div v-if="!ready">
                    <h4>Enter your username</h4>
                    <form @submit.prevent="addUser">
                        <div class="form-group row">
                            <input type="text" class="form-control col-9" v-model="username"
                                placeholder="Enter username here">
                            <input type="submit" value="Join" class="btn btn-sm btn-info ml-1">

                        </div>
                    </form>
                </div>
                <h2 v-else>{{username}}</h2>
                <div class="card bg-info" v-if="ready">
                    <div class="card-header text-white">
                        <h4>My Chat App <span class="float-right">{{connections}} connections</span></h4>
                    </div>
                    <ul class="list-group list-group-flush text-right">
                        <small v-if="typing" class="text-white">{{typing}} is typing</small>
                        <li class="list-group-item" v-for="message in messages">
                            <span :class="{'float-left':message.type === 1}">
                                {{message.message}}
                                <small>:{{message.user}}</small>
                            </span>
                        </li>
                    </ul>

                    <div class="card-body">
                        <form @submit.prevent="send">
                            <div class="form-group">
                                <input type="text" class="form-control" v-model="newMessage"
                                    placeholder="Enter message here">
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>

</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="/socket.io/socket.io.js"></script>

<script>
    // Client side Socket.IO object
    var socket = io();
    
    // Creating a new Vue Instance
    let vue = new Vue({
        
        // Adding a root element to our vue app
        el: '#app',

        // Declaring our data object with empty arrays and properties
        data: {
            newMessage: null,
            messages: [],
            typing: false,
            username: null,
            ready: false,
            info: [],
            connections: 0,
        },
        
        
        // Our vue created method
        created() {
            
            // Emitting 'leave' event on tab closed event.
            window.onbeforeunload = () => {
                socket.emit('leave', this.username);
            }
            
            // Listening to chat-message event emitted from the server and pushing to messages array
            socket.on('chat-message', (data) => {
                this.messages.push({
                    message: data.message,
                    type: 1,
                    user: data.user,
                });
            });
            
            
            // Listening to typing event emitted from the server and setting the data (username) to the UI
            socket.on('typing', (data) => {
                this.typing = data;
            });

             // Listening to stopTyping event emitted from the server and setting the typing property to false
            socket.on('stopTyping', () => {
                this.typing = false;
            });

             // Listening to 'joined' event emitted from the server and pushing the data to info array
            socket.on('joined', (data) => {
                this.info.push({
                    username: data,
                    type: 'joined'
                });

                // Setting a time out for the info array to be emptied
                setTimeout(() => {
                    this.info = [];
                }, 5000);
            });

            // Listening to 'leave' event emitted from the server and pushing the data to info array
            socket.on('leave', (data) => {
                this.info.push({
                    username: data,
                    type: 'left'
                });
                
                // Setting a time out for the info array to be emptied
                setTimeout(() => {
                    this.info = [];
                }, 5000);
            });

             // Listening to 'connections' event emitted from the server to get the total number of connected clients
            socket.on('connections', (data) => {
                this.connections = data;
            });
        },

        // Vue Watch hook
        watch: {
            
            // Watching for changes in the message inbox box and emitting the either 'typing' or 'stopTyping' event
            newMessage(value) {
                value ? socket.emit('typing', this.username) : socket.emit('stopTyping')
            }
        },

        //Vue Methods hook
        methods: {
            
            //The send method stores the user message and emit an event to the server.
            send() {
                this.messages.push({
                    message: this.newMessage,
                    type: 0,
                    user: 'Me',
                });

                socket.emit('chat-message', {
                    message: this.newMessage,
                    user: this.username
                });
                this.newMessage = null;
            },

            // The addUser method emit a 'joined' event with the username and set the 'ready' property to true.
            addUser() {
                this.ready = true;
                socket.emit('joined', this.username)
            }
        },

    });
</script>

</html>

Congratulations on getting here. You can subscribe to my channel to be notified when the video drops.

Conclusions

We have learned a lot with build a real-time chat app with Vuejs, socket.io, and Nodejs article.

Also, you can improve the code, add authentications, add groups, one to one chat, and add database management to manage all your new changes with session management too. I will be super excited to see your application.

An updated version of Build a Real-time Chat App with Vue 3, Socket.io and Nodejs using Vue 3 and added more features such as authentication, authorization, and database functionalities.

I hope you learnt something new with Vue, Node, Express, and Socket.IO. The full code is on GitHub, get it now.

Thank you for reading my article.

Here at my blog or medium I regularly write about backend development, digital marketing and content management system. To read my future posts simply join my publication or click ‘Follow’ Also feel free to connect with me via Twitter, Facebook, Instagram.

If you are interested in backend development (or you’re internet enthusiast) both (Mobile | Web | Desktop) videos subscribe to my Youtube channel, we will be posting a collection of help full tutorials and guides like this one for artisans.

If you enjoy this post, make sure to let us know and share it with your friends and subscribe to my growing channel.

Sharing is caring.

Whenever you're ready

There are 3 ways we can help you become a great backend engineer:

The MB Platform

Join 1000+ backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.

The MB Academy

The “MB Academy” is a 6-month intensive Advanced Backend Engineering BootCamp to produce great backend engineers.

Join Backend Weekly

If you like post like this, you will absolutely enjoy our exclusive weekly newsletter, Sharing exclusive backend engineering resources to help you become a great Backend Engineer.

Backend Tips, Every week

Backend Tips, Every week