DEV Community

Cover image for Building a Real-Time Chat Application with Socket.io, Express, React.js, and Chakra UI (Vite Setup)
Jagroop Singh
Jagroop Singh

Posted on

Building a Real-Time Chat Application with Socket.io, Express, React.js, and Chakra UI (Vite Setup)

In this blog, we’ll build a real-time chat application using Socket.io for bi-directional communication, Express.js for the server, React.js for the frontend, and Chakra UI for styling. We'll set up the project using Vite for fast development.

Key Features:

  • User ID based chat: A random userId is generated and stored in session storage.
  • Message display: Our messages appear on the left, and others' appear on the right, each with an icon.

Here is the demo of this project :

Socket demo

chat demo

Server socket logs

Let’s get started!

1. Setting Up the Project

Create A wrapper folder chat-app where our front-end and back-end resides:

mkdir chat-app
cd chat-app
Enter fullscreen mode Exit fullscreen mode

Backend Setup: Express.js with Socket.io
First, create a folder for your backend and initialize it with npm init -y:

mkdir chat-backend
cd chat-backend
npm init -y
Enter fullscreen mode Exit fullscreen mode

Now install the necessary packages:

npm install express socket.io
Enter fullscreen mode Exit fullscreen mode

Create the following file structure for the backend:

chat-backend/
│
├── server.js
└── package.json

Enter fullscreen mode Exit fullscreen mode

server.js - Express and Socket.io
Here’s how to set up the Express server with Socket.io:

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
  cors: {
    origin: 'http://localhost:5173',
    methods: ['GET', 'POST']
  }
});

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

  socket.on('sendMessage', (message) => {
    io.emit('receiveMessage', message);
  });

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

server.listen(4000, () => {
  console.log('Server listening on port 4000');
});

Enter fullscreen mode Exit fullscreen mode

This code creates an Express server that listens on port 4000 and sets up Socket.io to handle real-time communication. When a user sends a message (sendMessage event), it's broadcast to all connected clients.

Frontend Setup: Vite with React and Chakra UI

Create a Vite React project and follow below screenshots (create project in chat-app and not in chat-backend):

npm create vite@latest
Enter fullscreen mode Exit fullscreen mode

Creating project

Selecting Reactjs

Selecting Js

Navigate into the project folder and install the necessary dependencies:

cd chat-frontend
npm install socket.io-client @chakra-ui/react @emotion/react @emotion/styled framer-motion
Enter fullscreen mode Exit fullscreen mode

Now, let's create a basic structure for our chat application.

2. Implementing the Chat Frontend

The following will be the structure for the frontend:

chat-frontend/
│
├── src/
│   ├── components/
│   │   └── ChatBox.jsx
│   ├── App.jsx
│   └── main.jsx
├── index.html
└── package.json
Enter fullscreen mode Exit fullscreen mode

App.jsx

In App.jsx, set up Chakra UI and render the ChatBox component:

import { ChakraProvider, Box, Heading } from "@chakra-ui/react";
import ChatBox from "./components/ChatBox";

function App() {
  return (
    <ChakraProvider>
      <Box p={5}>
        <Heading as="h1" mb={6}>
          Real-Time Chat
        </Heading>
        <ChatBox />
      </Box>
    </ChakraProvider>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

ChatBox.jsx

This is where the main chat logic will go. We’ll use Socket.io to listen for messages and handle real-time updates. A random userId is generated and stored in session storage, and the chat UI is built using Chakra UI.

import React, { useState, useEffect } from "react";
import { Box, Input, Button, HStack, VStack, Text, Avatar } from "@chakra-ui/react";
import { io } from "socket.io-client";

const socket = io("http://localhost:4000");

const ChatBox = () => {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState("");
  const [userId, setUserId] = useState(null);

  useEffect(() => {
    // Generate or get userId from session storage
    let storedUserId = sessionStorage.getItem("userId");
    if (!storedUserId) {
      storedUserId = Math.random().toString(36).substring(7);
      sessionStorage.setItem("userId", storedUserId);
    }
    setUserId(storedUserId);

    // Listen for messages
    socket.on("receiveMessage", (message) => {
      setMessages((prevMessages) => [...prevMessages, message]);
    });

    return () => {
      socket.off("receiveMessage");
    };
  }, []);

  const sendMessage = () => {
    if (input.trim()) {
      const message = {
        userId,
        text: input,
      };
      socket.emit("sendMessage", message);
    //   setMessages((prevMessages) => [...prevMessages, message]);
      setInput("");
    }
  };

  return (
    <VStack spacing={4} align="stretch">
      <Box h="400px" p={4} borderWidth={1} borderRadius="lg" overflowY="auto">
        {messages.map((msg, index) => (
          <HStack key={index} justify={msg.userId === userId ? "flex-start" : "flex-end"}>
            {msg.userId === userId && <Avatar name="Me" />}
            <Box
              bg={msg.userId === userId ? "blue.100" : "green.100"}
              p={3}
              borderRadius="lg"
              maxW="70%"
            >
              <Text>{msg.text}</Text>
            </Box>
            {msg.userId !== userId && <Avatar name="Other" />}
          </HStack>
        ))}
      </Box>

      <HStack>
        <Input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Type a message"
        />
        <Button onClick={sendMessage} colorScheme="teal">
          Send
        </Button>
      </HStack>
    </VStack>
  );
};

export default ChatBox;
Enter fullscreen mode Exit fullscreen mode

How It Works:

  • Random userId: We generate a random string as userId and store it in sessionStorage. This ensures that even if you reload the page, you'll retain the same userId for the session.

  • Socket.io for messaging: The app listens for receiveMessage events from the server and displays incoming messages. When a user sends a message, it's emitted via Socket.io, and the UI updates in real-time.

  • Chakra UI styling: Messages are displayed in a box, with our messages aligned to the left (with a blue background) and others’ to the right (green background). Each message also has an avatar for a simple, personalised chat interface.

3. Running the Application

Start the backend:

cd chat-backend
node server.js
Enter fullscreen mode Exit fullscreen mode

Start the frontend:

cd chat-frontend
npm run dev
Enter fullscreen mode Exit fullscreen mode

Now, open http://localhost:5173 in multiple browser windows to test real-time chat functionality. You’ll see each user's messages displayed with their unique userId.

You’ve successfully built a real-time chat application using Socket.io, Express, React.js, and Chakra UI, with the project set up via Vite! 🎉

This app demonstrates the power of Socket.io for real-time communication 💬 and Chakra UI for a clean, responsive interface 📱.

You can expand this project by adding features like chat rooms 🏠, message persistence 💾, and user authentication 🔒.

That's all for this blog! Stay tuned for more updates and keep building amazing apps! 💻✨
Happy coding! 😊

Top comments (37)

Collapse
 
charlesr1971 profile image
Charles Robertson

This is a great starter app, but it would be really great, if you could show us how to set up an admin to multiple user chat app. So, there is one admin and he can chat with multiple users, but each user only sees his/her own messages & the response from the admin. The admin would be able to see all users in a list and then choose which user to interact with.
This would be a brilliant real world chat app tutorial, if you don’t think it’s too complicated.

Collapse
 
jagroop2001 profile image
Jagroop Singh

Great idea !!
But I think it's good for Bootcamp but it's too complicated for covering this in blog.

Collapse
 
charlesr1971 profile image
Charles Robertson • Edited

I tried creating this app in Angular, it worked pretty well, but after a while messages started to leak to the wrong user.

As I remember I sent:

userid
threadid 
messageid 
Enter fullscreen mode Exit fullscreen mode

As admin, I could then choose which user to send a message back to.
Of course if the userid in the client, doesn’t match then the message isn’t shown. But this seems a really inefficient way of pushing data. It would be far better to only send messages from the admin to a specific user.
However, I guess this is impossible, because a websocket cannot broadcast to specific clients? Or can it, if we take advantage of the room feature? Maybe if a room has the same id as the userid, then this would work?

Thread Thread
 
jagroop2001 profile image
Jagroop Singh

@charlesr1971 , you can create room with specific id for example jobId and add only those user's to socket room which you want to.

Thread Thread
 
charlesr1971 profile image
Charles Robertson

Is it possible to push a message back to an individual client, if multiple clients are listening to the same socket?
From what I know, a message is broadcast to every client that listens and then it is up to the front end code to filter those messages out. Obviously the userid is sent back with every message and if this matches the client userid, then the user can view that message.
But if I can set each room to a specific userid then presumably it will send the response back to the correct user?
But maybe I am misunderstanding how a room works?
I did read that the socket automatically allocates an incoming message to its own room, which is represented by a random id.

Collapse
 
works profile image
Web

@jagroop2001 ,Your blog makes difficult tasks feel like a piece of cake.

Collapse
 
jagroop2001 profile image
Jagroop Singh

Thanks for appreciation 🥳🧡.

It would be really helpful if you will react and share this with your friends.

Collapse
 
john12 profile image
john

@works is right.

Collapse
 
abdul_musavir_17097 profile image
Abdul Musavir

Very Helpfull

Collapse
 
jagroop2001 profile image
Jagroop Singh

@abdul_musavir_17097 , thanks .
If you really like my efforts please react and share.
It really motivates me to write more blogs.

Collapse
 
artwork_studios profile image
Artwork Studios

Please "react" and share lol

Collapse
 
mezieb profile image
Okoro chimezie bright

thanks a lot for sharing

Collapse
 
jagroop2001 profile image
Jagroop Singh

@mezieb You're welcome! 😊 I'm so happy my blog is useful for you!

Collapse
 
mezieb profile image
Okoro chimezie bright

yes i was integrating such functionality with socket.io,express in an angular project and you throwing more light to it help me structure things well.

Thread Thread
 
jagroop2001 profile image
Jagroop Singh

@mezieb
I’m glad to hear that! Integrating with Socket.IO and Express in an Angular project sounds exciting. If you need any more tips or have questions, feel free to ask! 😊

Collapse
 
john12 profile image
john

@jagroop2001 , I'm skeptical that creating a chatting application is that simple. Thank you for sharing this fantastic blog.

Collapse
 
jagroop2001 profile image
Jagroop Singh

Thanks @john12 🧡🧡

Collapse
 
avisheks profile image
Avishek sharma

This is nice bro 💯

Collapse
 
jagroop2001 profile image
Jagroop Singh

@avisheks , thanks

Collapse
 
chesedgamesonline profile image
ChesedGamesOnline

Great, but I don't understand much... Only know NodeJS. By the way how to export console applications with it?

Collapse
 
jagroop2001 profile image
Jagroop Singh

Thank !
However, I’m a bit confused about what you’re asking. Could you please clarify?

Collapse
 
chesedgamesonline profile image
ChesedGamesOnline

You're welcome. I mean how to set up everything like socket.io and the rest. Maybe make a video tutorial?

Collapse
 
neurabot profile image
Neurabot

Great.

Collapse
 
jagroop2001 profile image
Jagroop Singh

Thanks @neurabot

Collapse
 
lizardkinglk profile image
sndp

Nice post. Thank you. Can you tell me how/where can one deploy this application?

Collapse
 
jagroop2001 profile image
Jagroop Singh

@lizardkinglk , Sure I am also thinking about that blog.

Collapse
 
martinbaun profile image
Martin Baun

It's really helpful to see a detailed breakdown of the process. Nice!

Collapse
 
jagroop2001 profile image
Jagroop Singh

@martinbaun, Glad to hear my blog is helpful for you! 😄 Thank you!

Collapse
 
web-dev-codi profile image
Web-Dev-Codi

Beautiful post right here, Thank you for putting this together

Collapse
 
jagroop2001 profile image
Jagroop Singh

@web-dev-codi ,Thank you so much! I'm glad you liked it.

Collapse
 
wafi_nashwan_8a9b96b6e027 profile image
wafi nashwan

There's an awesome database library for stuff like this, on the front end! Check it

Collapse
 
jagroop2001 profile image
Jagroop Singh

@wafi_nashwan_8a9b96b6e027 ,That sounds great! I'll definitely check it out! 🚀

Collapse
 
abass_koroma_c52437027be9 profile image
Abass Koroma

Thanks for your opinion I'm so happy to meet different people who I can chat with.

Collapse
 
jagroop2001 profile image
Jagroop Singh

👍

Collapse
 
jaikumar007 profile image
JAIKUMAR DEWANGAN

Can anyone share whole files structure in which mix frontend and backend

Collapse
 
john12 profile image
john

@jaikumar007 , I think this approach is not according to IT Industry standard.

Collapse
 
ronewaonly profile image
Ronewa Maselesele

This was a sample and easy walk by through memory lane. I needed a refresher thank you .