Real Time Chat with React and Socket.IO
Real time chat with react and Socket.io

Real Time Chat with React and Socket.IO

Dead Simple Chat Team

Table of Contents

In this article we are going to create a real time chat application with reactjs, socket.io and Expressjs

Let us start building the application

Pre-requisites

You need to have basic knowledge of these technologies.

  • ReactJs
  • Socket.io
  • NodeJs
  • JavaScript

Installation

Create a new directory called the react-chat-app and cd in the it. Then type the below command to initialize a new project

npm init

This would ask you a few basic questions and create an package.json file

initializing the application

Now, type the below code to install the dependencies

npm install react react-dom socket.io socket.io-client express --save
installing required dependencies

Now the package.Json should look something like this:

{
  "name": "react-chat-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "DeadSimpleChat Team",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "socket.io": "^4.6.1",
    "socket.io-client": "^4.6.1"
  }
}
package.json

Now we have installed everything we need. Let us start coding

Creating the Server

In the root folder create a file and name it index.js and open the folder in your favorite text editor. I am using visual studio code.

Then in the index.js paste the following code

const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = socketIO(server);

const PORT = process.env.PORT || 4000;

app.get('/', (req, res) => {
  res.send('Hello World!')
});

server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
index.js

What are we doing here

  1. We are importing the required modules for our application namely: express, http, socketIO and path
  2. We are creating an HTTP server with express
  3. Then we are initializing socket.io on the HTTP server and defining a port to listen on
  4. Then we are creating a get route and sending Hello world when you go to localhost://4000

You can go to localhost:4000 and you will see there the hello world. Till now we have created a http server and initialized an socket.io instance on it

localhost 4000

'

Create the public folder

Let us now create a folder in the root and name it public and we will write code to render it using express.static middleware. Write this code in your index.js

app.use(express.static(path.join(__dirname, 'public')));
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = socketIO(server);

const PORT = process.env.PORT || 4000;

app.use(express.static(path.join(__dirname, 'public')));

app.get('/', (req, res) => {
  res.send('Hello World!')
});

server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
index.js

Creating HTML file

Now create a file in your public folder and name it index.html and write the below code there

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>React Socket.IO Chat</title>
</head>
<body>
  <div id="root"></div>
  <script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
  <script src="https://unpkg.com/socket.io-client@4/dist/socket.io.min.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

  <script type="text/babel" src="/client/client.js"></script>
</body>
</html>
index.html

What are we doing here

we are adding all the dependencies for the front end code though CDN. you can choose to npm install these as well then just above the closing body tag we are including the client.js file.

Creating the client.js file

Let us next create the client.js file

Now, create another  folder in the root and call it  client and create a file inside the client folder and name it client.js

Open the client .js file and paste the below code there

// client/client.js
const { useState, useEffect } = React;
const { io } = window;

const SOCKET_SERVER_URL = 'http://localhost:4000';

const App = () => {
  const [message, setMessage] = useState('');
  const [messages, setMessages] = useState([]);
  const [socket, setSocket] = useState(null);

  useEffect(() => {
    const newSocket = io(SOCKET_SERVER_URL);
    setSocket(newSocket);

    newSocket.on('chat message', (msg) => {
      setMessages((prevMessages) => [...prevMessages, msg]);
    });

    return () => {
      newSocket.disconnect();
    };
  }, []);

  const handleMessageChange = (e) => {
    setMessage(e.target.value);
  };

  const handleSendMessage = (e) => {
    e.preventDefault();
    if (message.trim()) {
      socket.emit('chat message', message);
      setMessage('');
    }
  };

  return (
    <div>
      <h1>React Socket.IO Chat</h1>
      <ul>
        {messages.map((msg, i) => (
          <li key={i}>{msg}</li>
        ))}
      </ul>
      <form onSubmit={handleSendMessage}>
        <input
          type="text"
          value={message}
          onChange={handleMessageChange}
          placeholder="Start typing the message"
        />
        <button type="submit">Send Message</button>
      </form>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
client.js

What are we doing here

  1. We are importing the required dependencies these include the useState and useEffect from React and Socket.io client side library
  2. Then we are declaring the socket server url to be 4000
  3. After that we are creating the App component
  4. We are declaring the variables for current message, message list and socket
  5. We are using the useEffect to listen to the socket server url and setting up listeners for the incoming sockets
  6. Creating a function to take the input form the front end and
  7. Create a function to send the message to the server
  8. Then we come to the return function
  9. Setting up basic HTML and declaring the H1
  10. rendering the messages in a list
  11. creating a form to take the messge input form the user and calling the handleMessageChange function

Lastly let us edit the index.js file and add the following code

app.get('/client/client.js', (req, res) => {
    res.sendFile(path.join(__dirname, 'client', 'client.js'));
  });

and delete the default get route which sent the 'Hello world'

// Delete this code
app.get('/', (req, res) => {
  res.send('Hello World!')
});

Also write the code to handle socket.io connections in the index.js file

io.on('connection', (socket) => {
  console.log('A user connected');

  socket.on('chat message', (msg) => {
    io.emit('chat message', msg);
  });

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

The completed index.js file looks like:

const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = socketIO(server);

const PORT = process.env.PORT || 4000;

app.use(express.static(path.join(__dirname, 'public')));

app.get('/client/client.js', (req, res) => {
    res.sendFile(path.join(__dirname, 'client', 'client.js'));
  });

  io.on('connection', (socket) => {
    console.log('A user connected');
  
    socket.on('chat message', (msg) => {
      io.emit('chat message', msg);
    });
  
    socket.on('disconnect', () => {
      console.log('A user disconnected');
    });
  });


server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
index.js

Now we have completed the app the app looks like this

completed app

You can open the app in multiple browser windows and send the messages in real time and they would appear like

App sending messages

This is a simple demonstration, you can add styling to it and make it look good.

Bonus

As a bonus feature let us add a feature to show usernames of the user sending the message

In the index.js update the code to include the message senders username

  socket.on('chat message', ({ username, message }) => {
    io.emit('chat message', { username, message });
  });
index.js

The complete index.js code looks like

const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = socketIO(server);

const PORT = process.env.PORT || 5000;

app.use(express.static(path.join(__dirname, 'public')));

app.get('/client/index.js', (req, res) => {
  res.sendFile(path.join(__dirname, 'client', 'index.js'));
});

io.on('connection', (socket) => {
  console.log('A user connected');

  socket.on('chat message', ({username, message}) => {
    io.emit('chat message', {username, message});
  });

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

server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
index.js

On the client side let us update the code and add a new state variable to store the username of the sender.

const [username, setUsername] = useState('');

and add an input field inside the form to enable the user to create a username

<form onSubmit={handleSendMessage}>
  <input
    type="text"
    value={username}
    onChange={(e) => setUsername(e.target.value)}
    placeholder="enter/create a username"
    />
 <input
    type="text"
    value={message}
    onChange={handleMessageChange}
    placeholder="Start typing the message"
    />
 <button type="submit">Send Message</button>
</form>
client.js

Now, edit the handle message function to include the username when sending the message to the user

const handleSendMessage = (e) => {
    e.preventDefault();
    if (message.trim() && username.trim()) {
        socket.emit('chat message', { username, message });
        setMessage('');
    }
};
client.js

Lastly edit the list code to show the username of the user sending the message

<ul>
    {messages.map((msg, i) => (
    <li key={i}>
    <strong>{msg.username}: </strong>
    {msg.message}</li>
    ))}
</ul>
index

Here is what the app looks like:

Completed Chat application

You might be interested in some of our other articles

Conclusion

In this article we made a group chat application with react and socket.io and express and Nodejs

We made a group chat application and as a bonus feature we added the username of the user sending the message

Thanks for reading. Below is the complete code for the application

Complete Code

Here is the complete code for the application

Index.js

const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = socketIO(server);

const PORT = process.env.PORT || 4000;

app.use(express.static(path.join(__dirname, 'public')));

app.get('/client/client.js', (req, res) => {
    res.sendFile(path.join(__dirname, 'client', 'client.js'));
  });

  io.on('connection', (socket) => {
    console.log('A user connected');
  
    socket.on('chat message', ({username, message}) => {
      io.emit('chat message', {username, message});
    });
  
    socket.on('disconnect', () => {
      console.log('A user disconnected');
    });
  });


server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
index.js

Client.js

const { useState, useEffect } = React;
const { io } = window;

const SOCKET_SERVER_URL = 'http://localhost:4000';

const App = () => {
  const [message, setMessage] = useState('');
  const [messages, setMessages] = useState([]);
  const [socket, setSocket] = useState(null);
  const [username, setUsername] = useState('');

  useEffect(() => {
    const newSocket = io(SOCKET_SERVER_URL);
    setSocket(newSocket);

    newSocket.on('chat message', (msg) => {
      setMessages((prevMessages) => [...prevMessages, msg]);
    });

    return () => {
      newSocket.disconnect();
    };
  }, []);

  const handleMessageChange = (e) => {
    setMessage(e.target.value);
  };


  const handleSendMessage = (e) => {
    e.preventDefault();
    if (message.trim() && username.trim()) {
      socket.emit('chat message', { username, message });
      setMessage('');
    }
  };

  return (
    <div>
      <h1>React Socket.IO Chat</h1>
      <ul>
        {messages.map((msg, i) => (
          <li key={i}>
             <strong>{msg.username}: </strong>
            {msg.message}</li>
        ))}
      </ul>
      <form onSubmit={handleSendMessage}>
        <input
          type="text"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
          placeholder="enter/create a username"
        />
        <input
          type="text"
          value={message}
          onChange={handleMessageChange}
          placeholder="Start typing the message"
        />
        <button type="submit">Send Message</button>
      </form>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
client.js

Index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>React Socket.IO Chat</title>
</head>
<body>
  <div id="root"></div>
  <script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
  <script src="https://unpkg.com/socket.io-client@4/dist/socket.io.min.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

  <script type="text/babel" src="/client/client.js"></script>
</body>
</html>
index.html

package.json

{
  "name": "react-chat-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "DeadSimpleChat Team",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "socket.io": "^4.6.1",
    "socket.io-client": "^4.6.1"
  }
}
package.json