Vue 3+ WebSockets: How to build a realtime chat application in 15 minutes

In this tutorial, we will see how to quickly build a realtime chat application with an authentication system using Vue 3, vue-router and a free SocketsBay account.

What is Vue.js ?

Here is an overview of this amazing and lightweight javascript framework:

Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.

What is a WebSocket ?

WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection. The WebSocket protocol enables interaction between a web browser (or other client application) and a web server with lower overhead than half-duplex alternatives such as HTTP polling, facilitating real-time data transfer from and to the server.

This is made possible by providing a standardised way for the server to send content to the client without being first requested by the client, and allowing messages to be passed back and forth while keeping the connection open. In this way, a two-way ongoing conversation can take place between the client and the server.

The communications are done over TCP port number 80 (or 443 in the case of TLS-encrypted connections), which is of benefit for those environments which block non-web Internet connections using a firewall.

What is SocketsBay.com ?

It is a free service allowing you to create a WebSocket server without the pain of configuring the server. In just a few minutes you can have a working secure web socket server. That way, you can focus on your app and users. For this tutorial you have to prepare a free account here.

Let’s start !

First, we are going to initialise a new Vue project by using the great tool Vue Cli ( https://cli.vuejs.org/ ). At the time of the article, last version is 5.0.8

If you don’t already have vue cli installed, let’s install it:

npm install -g @vue/cli

Now that you have vue cli installed, let’s create a new project.

vue create vue-chat

choose “Manually select features” and check the following:

Choose Babel, Router, Vuex and ( optionally ) CSS Pre-processors. Press Enter key after.

Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) 

Press “Y” and after that the Enter key.

If you checked the CSS Pre-processors, the wizard will now ask you what preprocessor would you like. I’m a LESS fan, so just choose LESS and press Enter

At the next question just answer whatever you want, it is not relevant for our tutorial.

And — finally:

Save this as a preset for future projects? (y/N)

just press ’n’ and Enter to start installing everything you need!

After this, just write

cd vue-chat

and start the application

vue run serve

and if everything was all right, the browser will start at the url ‘http://localhost:8080’ and the default Vue page will appear.

Default Vue starting page

Now let’s prepare our project !

Before building the chat page, we need to create a new app in our SocketsBay account. Click on the “Create new app” button in your account

Click on the “Create new app” button in your account

You will receive a API key and we will keep it in mind.

Now let’s get back to our project! Follow the next steps to clean our code a little:

1. Delete the src/components folder! we don’t need it
2. Delete the src/views/AboutView.vue file! we don’t need it
3. In the file src/router/index.js delete the part with “about”:

remove this part of code, we don’t need it :)

4. In the file src/App.vue, delete this upper html code:

remove this part also

5. Delete everything in the src/views/HomeView.vue file and write this instead:

<template>
<div>
Here we will write our awesome code!
</div>
</template>

<script>
export default {
name: 'HomeView',

}
</script>

<style lang="less">
</style>

Verification:

Save and check the url http://localhost:8080/ and if you see something like this, everything is ready:

ready for action!

Add an image asset to the code

Just “save as” this image and save it in src/assets/background.jpg folder from your project. Alternatively you can download the image from here. It’s nice and it will make your work more good looking:

Perfect! Now let’s write some code!

Now, everything that we will do it will be in src/views/HomeView.vue file.

First step is to add our variables. Let’s add this in the data part of the vue component:

data(){
return {
sockets_bay_api_key: "", //here you must insert your api key from Sockets Bay
connection_ready: false,
connection_error: false,
nickname: "",
websocket: null,
new_message: "",
messages: []
}
},

the code is self explanatory. We keep the nickname of the connected user, the connection status and the messages displayed on the screen.

Now let’s add the html code. It is divided in 3 parts:

  1. the header part ( title and connection status )
  2. the chat message list
  3. the sending part ( where we write and send messages )
<template>
<div class="container">
<h1>Awesome chat - <span class="connection_ready" v-if="connection_ready">Connection ready!</span></h1>

<div class="messages" id="messages">
<div class="message-container">
<h1 class="error" v-if="connection_error"> Connection error! </h1>
<div v-for="(m,idx) in messages" :key="'m-' + idx" style="clear:both">
<div :class="{ 'msg-from-me' : m.from=='me', 'msg-from-other' : m.from=='other'}">
{{m.message}}
</div>
</div>
</div>
</div>

<div class="send-zone">
<input v-model="new_message" type="text" placeholder="Type a message" @keyup.enter="send_message"/>
</div>
</div>
</template>

For a quick preview, it should look like this at the end:

But to get this app to be that pretty, we have to add this CSS code, in the style part:

<style lang="less">
body{
background: #111b21;
}
.container{
display: flex;
flex-direction: column;
margin: 0 auto;
max-width: 768px;
min-height: 98vh;
position: relative;

h1{
padding: 0px;
height: 20px;
color: white;
font-size: 20px;
text-transform: uppercase;

.connection_ready{
color: greenyellow;
}
}

.messages{
height: 80vh;
overflow-y: scroll;
background: url(@/assets/background.jpg) no-repeat center;
background-size: cover;

.msg-from-me {
border-radius: 7.5px;
max-width: 65%;
font-size: 16px;
line-height: 19px;
color: #e9edef;
background: fade( #046a62, 90%);
padding: 5px;
margin: 20px 20px 5px 0px;

float: right;
}
.msg-from-other {
border-radius: 7.5px;
max-width: 65%;
font-size: 16px;
line-height: 19px;
color: #e9edef;
background: fade( #202c33, 90%);
padding: 5px;
margin: 20px 0px 5px 20px;
float: left;
}
}
.send-zone{
height: 62px;
background: #202c33;
display: flex;

input[type='text']{
padding: 9px 12px 11px;
margin: 5px 10px;
border: 1px solid #2a3942;
background: #2a3942;
border-radius: 8px;
font-size: 15px;
flex-grow: 1;
color: white;
}

}
}

</style>

Good ! now the app is looking great! And now, for the functionality:

export default {
name: 'HomeView',
data(){
return {
sockets_bay_api_key: "", //here you must insert your api key from Sockets Bay
connection_ready: false,
connection_error: false,
nickname: "",
websocket: null,
new_message: "",
messages: []
}
},
methods:{
init_chat() {
//ask for a nickname
if(this.nickname == "") this.nickname = prompt("Enter a nickname:");

//connect to Sockets Bay
var sockets_bay_url = `wss://socketsbay.com/wss/v2/100/${this.sockets_bay_api_key}/`;
this.websocket = new WebSocket(sockets_bay_url);
//
this.websocket.onopen = this.onSocketOpen;
this.websocket.onmessage = this.onSocketMessage;
this.websocket.onerror = this.onSockerError;
},
onSocketOpen(evt){
this.connection_ready = true;
},
onSocketMessage(evt){
//we parse the json that we receive
var received = JSON.parse(evt.data);
//check if it's our message or from a friend
this.messages.push( { from: "other", message: received.message } );
//scroll to the bottom of the messages div
const messages_div = document.getElementById('messages');
messages_div.scrollTo({top: messages_div.scrollHeight, behavior: 'smooth'});
},

onSockerError(evt){
this.connection_error = true;
},

send_message() {
var to_send = { from: this.nickname, message: this.new_message };
this.websocket.send( JSON.stringify(to_send) );
this.messages.push( { from: "me" , message: this.new_message } );
this.new_message = "";
}
},
mounted() {
this.init_chat();
}
}

Some code explinations:

When the page is mounted, we call init_chat method. This one establish connection with the sockets server and sets up the methods:

  1. onSocketOpen -> this method is called when the connection to the WebSocket server is opened. Here we just made the connection_ready variable true, to show the CONNECTION READY! message in the header
  2. onSocketMessage -> this method is called when we receive a message from the WebSocket server. Because we send messages as JSON packages, we first parse the json string received and then we decide if we add the message as our message or as on “other” friend message. And we just add this to our messages array. Vue will take care of the rest :)
  3. onSocketError -> this method is called when we have a connection problem. In this example it will just show a message in the chat area with a “Connection error!” message

On the text input we called the method “send_message” when we press the enter key. This method creates a json with this template:

{ from: "sender's nickname", message: "his message to the world!"}

and we just send it to the SocketsBay server using the send method.

Let’s see it in action!

You can also find the code on GitHub:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store