DEV Community

Cover image for Create a realtime chat app with React hooks, socket.io and NodeJS
Deepinder Singh
Deepinder Singh

Posted on • Originally published at deepinder.me

Create a realtime chat app with React hooks, socket.io and NodeJS

In this tutorial, we'll learn how to build a real-time chat application with React Hooks, Socket.IO, WebSockets, ExpressJS and NodeJS. This would also work with React Native.

This might be the most searched query among all developers on how to make a live chat application with React and Socket.io .We will be using expressJS on top of NodeJS as a backend.

Read more of these stories on my website.

Creating a NodeJS Express App

Let’s start by creating a nodejs project first.

Create a new directory and then enter it.

mkdir socketio-node
Enter fullscreen mode Exit fullscreen mode

This will create a empty folder with name socketio-node.

We’re going to use the Node.JS web framework expressJS. Make sure NodeJS is installed on your system.

First let’s create a package.json manifest file that describes our project.
Create a file named package.json and paste the below code into it.(You can also do it with npm init)

{  
    "name": "socketio-node",
    "version": "0.0.1",
    "description": "my first socket.io app",
    "dependencies": {}
}
Enter fullscreen mode Exit fullscreen mode

Now, in order to easily populate the dependencies property we need to install express, type this in the terminal.

npm install express
Enter fullscreen mode Exit fullscreen mode

It will install and add the latest version of express into our project and your dependencies will now look like. The version can be different depending on the latest version at the time you install it.

"dependencies": {
  "express": "^4.17.1"
}
Enter fullscreen mode Exit fullscreen mode

Now that express is installed we can create an index.js file that will setup our application.

const app = require('express')();
const http = require('http').createServer(app);

app.get('/', (req, res) => {
  res.send('<h1>Hey Socket.io</h1>');
});

http.listen(3000, () => {
  console.log('listening on *:3000');
});
Enter fullscreen mode Exit fullscreen mode

This code is explained in the following steps:

  • Express initializes app to be a function handler that you can supply to an HTTP server (as seen in line 2).
  • We define a route handler / that gets called when we hit our website home.
  • We make the http server listen on port 3000.

If you run node index.js you should see the following:
node server listening on port 3000node server listening on port 3000

Opening http://localhost:3000 in browser would look like:
node server serving html on 3000

Integrating Socket.io with NodeJS

Now let’s integrate socket.io into our node app. Firstly, we need to install socket.io dependency into our app. Run this in the terminal.

npm install socket.io
Enter fullscreen mode Exit fullscreen mode

This will install the module and add the dependency to package.json. Now let’s edit index.js to add it:

const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http, {
  cors: {
    origins: ['http://localhost:3001']
  }
});

app.get('/', (req, res) => {
  res.send('<h1>Hey Socket.io</h1>');
});

io.on('connection', (socket) => {
  console.log('a user connected');
  socket.on('disconnect', () => {
    console.log('user disconnected');
  });
});

http.listen(3000, () => {
  console.log('listening on *:3000');
});
Enter fullscreen mode Exit fullscreen mode

Notice that I initialize a new instance of socket.io on line 3 by passing the http (the HTTP server) object and the cors options(updated for socket.io v3) to allow our react localhost url, you can put in the url or your frontend client, in my case it was localhost:3001
Then I listen on the connection and disconnection events for incoming sockets, and I log it to the console.

Our Backend is good to go for now, we will come back to our node code when we will implement more events further on.

Creating a React app

Let’s start by creating an React app now. I will be creating a new React app from scratch with create-react-app, while most of you would already have one created with you.
Those who already have a working React app can skip the following code:

npx create-react-app socketio-react
Enter fullscreen mode Exit fullscreen mode

(npx comes with npm 5.2+ and higher, see instructions for older npm versions)
This would install the latest version of CRA and create a new template React app from scratch.

Now let’s add socket.io dependency in our React app.

cd socketio-react
npm install socket.io-client
Enter fullscreen mode Exit fullscreen mode

This would install the latest socket.io-client library in our React app.

Creating a socket client service

Now let’s start by creating a file to handle socket.io connection. I would create a root level file named socketio.service.js and include that in the src folder.

You can create the file by running the following command.

cd src
touch socketio.service.js
Enter fullscreen mode Exit fullscreen mode

This would create a file named socketio.service.js . The directory structure would look something like this. This is just a simple one page demo, so i added the file into the src folder like this.
file structure of project
Now, go into the socketio.service.js file and import the following:

import { io } from 'socket.io-client';
Enter fullscreen mode Exit fullscreen mode

Now let’s add the socket endpoint/url that we would connect the socket.io client to the backend. We will start by creating a .env file in the root of the folder which would our environment variables.

touch .env
Enter fullscreen mode Exit fullscreen mode

.env file location
We will add the following url in .env file

REACT_APP_SOCKET_ENDPOINT=http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

We have to write REACT_APP as a prefix as it is needed by create-react-app. For more details you can check this link.

Let's start by writing our socketio.service.js and write a socket init function.

import { io } from 'socket.io-client';

let socket;

export const initiateSocketConnection = () => {
    socket = io(process.env.REACT_APP_SOCKET_ENDPOINT);
    console.log(`Connecting socket...`);
}
Enter fullscreen mode Exit fullscreen mode

This will declare a variable named socket and then after calling the initiateSocketConnection function, socket connect would be initialized on the URL provided in .env file and socket variable would be containing the connected socket object.

We have to use the variables inside .env file like this process.env.yourvariablename.

Since we have created a function, let's call it from our Component.

Start by opening App.js file and lets make use of the hooks. We will use useEffecthook which would only run once on render since we have to init the socket connection only once.

import { useEffect } from 'react';

function App() {

  useEffect(() => {
    initiateSocketConnection();
  }, []);

}
Enter fullscreen mode Exit fullscreen mode

Doing this would create a socket connection only once on component render and create our connection.

Note: I am importing only { useEffect } from 'react'; but not import React, {useEffect} from 'react'; since i am on React 17 and it doesn't need that, if you are on old versions(< 17) you might end up importing the last one.

We will run the React app now using

PORT=3001 npm start
Enter fullscreen mode Exit fullscreen mode

I used port prefix 3001 as CRA runs on 3000 by default and NodeJS is also running on that port.
You can see the socket connected and our node app console showing a user connected when we open our browser tab running the React app
logging of nodejs server

Disconnection

Now, let's try to disconnect the socket, we will use the cleanup function of the hooks.

import { useEffect } from 'react';

function App() {

  useEffect(() => {
    initiateSocketConnection();
    return () => {
      disconnectSocket();
    }
  }, []);

}
Enter fullscreen mode Exit fullscreen mode

In your socketio.service.js file add this for disconnection

export const disconnectSocket = () => {
  console.log('Disconnecting socket...');
  if(socket) socket.disconnect();
}
Enter fullscreen mode Exit fullscreen mode

This will disconnect our socket as soon as the component gets destroyed. Also, the socket would get disconnected when we close the tab automatically, whether we handle it here or not, tab closing is handled by default.

When you disconnect the socket or close the webpage, you can see user disconnected message on console.

With this, we have completed our initialization and disconnection of sockets. Now we will learn about how to emit and listen to events.

Event Handling

Let’s register an event called my message inside our index.js node file and console the data and we will emit the same event from React app.

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

  console.log('a user connected');

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

  socket.on('my message', (msg) => {
    console.log('message: ' + msg);
  });
});
Enter fullscreen mode Exit fullscreen mode

And let’s emit the same event from React code in socket.service.js

export const subscribeToChat = (cb) => {
    socket.emit('my message', 'Hello there from React.');
}
Enter fullscreen mode Exit fullscreen mode

We will call this function from useEffect where we initialized our socket connection in App.js

useEffect(() => {
    initiateSocketConnection();
    subscribeToChat((err, data) => {
      console.log(data);
    });
    return () => {
      disconnectSocket();
    }
  }, []);
Enter fullscreen mode Exit fullscreen mode

This code would emit the event named my message and it would print the following on our node console. You can see the message ‘Hello there from React’. Our custom events are now working.
message from react on node server

Broadcasting Event

Now, let’s emit an event from the server side to client side. We will broadcast the event to all connected users. We will broadcast the same message that we received from client and prepend a server string to it.

io.on('connection', (socket) => {
  socket.on('my message', (msg) => {
    io.emit('my broadcast', `server: ${msg}`);
  });
});
Enter fullscreen mode Exit fullscreen mode

This would emit the message received to all connected sockets.

Let’s add an listener for my broadcast event on our React app now.

export const subscribeToChat = (cb) => {
    socket.emit('my message', 'Hello there from React.');

    socket.on('my broadcast', msg => {
        return cb(null, msg);
    });
}
Enter fullscreen mode Exit fullscreen mode

Here, we receive the my broadcast event and call the registered callback in App.js
Since we already had a console.log written in App.js subscription, it will print the message received from server.

You can check your browser console, it would print something like this. It prints a message from server, that emitted the broadcast.
view of react app in browser
We have covered the basic parts of connecting an node socket.io app with an React app.

BONUS: Authentication

You can also send authentication parameters to the Backend when connecting to the socket by using auth object in options in a connection.

export const initiateSocketConnection = (room) => {
    socket = io(process.env.REACT_APP_SOCKET_ENDPOINT, {
      auth: {
        token: 'cde'
      },
    });
    console.log(`Connecting socket...`);
}
Enter fullscreen mode Exit fullscreen mode

I am sending token key here. You can use any key you want, to provide auth token or any other key.

To fetch this information on the Backend, we have to do it like this:

io.on('connection', (socket) => {
  let token = socket.handshake.auth.token;
});
Enter fullscreen mode Exit fullscreen mode

This would return the value cde passed by Frontend.

You can check all of the above written example code at my github.

This concludes my article about creating a real time application with React and Socket.io with NodeJS and ExpressJS.

The next part of this article which explains the concept of rooms, realtime one to one chat and group chat is here.

Do write down your reviews and remember to subscribe for more content like this.

Read more of these stories on my website.

Liked my work? Buy me a coffee.

Top comments (0)