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.
Creating a NodeJS Express App
Let’s start by creating a node.js
project first.
Create a new directory
and then enter it.
mkdir socketio-node
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": {}
}
Now, in order to easily populate the dependencies
property we need to install express
, type this in the terminal.
npm install express
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"
}
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');
});
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 3000
Opening https://localhost:3000 in browser would look like:
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
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: ['https://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');
});
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
(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
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
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.
Now, go into the socketio.service.js
file and import the following:
import { io } from 'socket.io-client';
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
We will add the following url in .env
file
REACT_APP_SOCKET_ENDPOINT=https://localhost:3000
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...`);
}
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 useEffect
hook 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();
}, []);
}
Doing this would create a socket connection only once on component render and create our connection.
We will run the React app now using
PORT=3001 npm start
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
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();
}
}, []);
}
In your socketio.service.js
file add this for disconnection
export const disconnectSocket = () => {
console.log('Disconnecting socket...');
if(socket) socket.disconnect();
}
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);
});
});
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.');
}
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();
}
}, []);
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.
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}`);
});
});
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);
});
}
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.
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...`);
}
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;
});
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 explains the concept of rooms, realtime one to one chat and group chat. Continue reading in Part 2 of Creating a Real-Time Chat App with React Hooks, Socket.io, and Rooms.
Liked my work. Buy me a coffee.
Do write down your reviews or send in a mail from the contact form if you have any doubts and do remember to subscribe for more content like this.