Prerequisites
Before we begin, please make sure you have the following software installed on your system. These tools are essential for developing and running the application we will build:
- Node.js: This is the runtime environment required to run JavaScript on the server side. Download it from the official Node.js website.
- NPM: Node.js’s package manager, which is used to install libraries like React. It comes bundled with Node.js.
- Vite: A build tool that facilitates scaffolding new projects and running them locally. Install it globally by running
npm install -g create-vite
. - Visual Studio Code: This is a lightweight but powerful source code editor which runs on your desktop. Download it from Visual Studio Code official site.
- Source Code: You can find the source code for this tutorial on GitHub.
Creating a Server with Express.js and Socket.io
Socket.io is a JavaScript library that enables real-time, bidirectional and event-based communication between web clients and servers. It is built on top of the WebSockets protocol and provides a simple API for sending and receiving messages between clients and servers.
Expres.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. It is designed to make the process of building web applications and APIs easier and more efficient.
Start by creating a new directory called server
and initialize a new npm project:
mkdir server
cd server
npm init -y # Automatically agree to default settings
Install the necessary packages by running: npm install express socket.io cors nodemon
.
- Express: A web server framework.
- Socket.io: Enables real-time bidirectional communication.
- CORS: Middleware that allows cross-origin requests.
- Nodemon: A development tool that automatically restarts the server when code changes.
Create an index.js
file with the following server setup:
const express = require("express");
const cors = require("cors");
const app = express();
const port = 3000;
const messages = ["Initial message from server"]; // Holds the messages on the server
// Enabling CORS for any unknown origin
app.use(cors());
const server = app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
const io = require("socket.io")(server, { cors: { origin: "*" } });
// Listen for new connection
io.on("connection", (socket) => {
console.log(`User connected ${socket.id}`);
// Send the messages to the client
socket.emit("messages", { messages }); // First parameter is the event name, second is the data
// Listen for new message from the client
socket.on("newMessage", (data) => {
messages.push(data);
io.emit("messages", { messages }); // Send the updated messages to all clients
});
// Listen for disconnection
socket.on("disconnect", () => {
console.log(`User disconnected ${socket.id}`);
});
});
Socket.io requires an HTTP server like Express.js and Socket.io is going to run on top of that.
CORS is configured to allow requests from any origin, which is necessary for development environments where the client and server are hosted separately.
The server is going to listen on client connection. When a client connects, server is sending all messages stored on server with event name messages
.
When a message is received from a client with event name newMessage
, the message is stored in the server and all clients are updated with the new message.
When everything is together now you can execute your server part of the application with nodemon index.js
.
You can test your server via Postman by selecting protocol SocketIO
and configuring event names. The server runs under http://localhost:3000
.
Create a Client with React
React is a popular JavaScript library for building user interfaces. It allows developers to create reusable UI components and manage the state of the application efficiently.
After setting up the server, let's move on to creating the client application using React. We'll use Vite for project scaffolding due to its performance benefits and simplicity. Run npm create vite@latest client -- --template react
to create a new project in folder client
. From the folder run npm install
to install all dependencies. Additionaly install a client for Socket.io npm install socket.io-client
and component library Chakra UI npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion
to make the application look better. Vite scaffolding includes some default CSS, which we will replace with our own in App.js
:
import { useEffect, useState } from "react";
import "./App.css";
import openSocket from "socket.io-client";
import {
Container,
Text,
Input,
Button,
ChakraProvider,
Flex,
Stack,
} from "@chakra-ui/react";
function App() {
const [newMessage, setNewMessage] = useState("");
const [messages, setMessages] = useState([]);
const [socket, setSocket] = useState(null);
useEffect(() => {
const socketInstance = openSocket("http://localhost:3000"); // Connect to the server
setSocket(socketInstance);
socketInstance.on("connect", () => {
console.log("Connected to server");
});
socketInstance.on("messages", (data) => {
setMessages(data.messages);
});
return () => {
socketInstance.disconnect(); // Clean up the socket connection
};
}, []);
const handleSendMessage = () => {
socket.emit("newMessage", newMessage);
setNewMessage("");
};
return (
<ChakraProvider>
<Container w="40%" pt={2}>
<Flex>
<Input
placeholder="Your message"
value={newMessage}
onChange={(event) => setNewMessage(event.target.value)}
/>
<Button ml={2} colorScheme="blue" onClick={handleSendMessage}>
Send
</Button>
</Flex>
<Stack spacing={3}>
{messages.map((message, index) => (
<Text key={`${message}-${index}`} fontSize="md">
{message}
</Text>
))}
</Stack>
</Container>
</ChakraProvider>
);
}
export default App;
In the useEffect()
hook we are connecting to the server and listening for the messages. When the component is unmounted we are disconnecting from the server.
When the user clicks on the button Send
the message is sent to the server with event name newMessage
. The message is stored in the server and all clients are updated with the new message.
We are ready to start the application with npm run dev
but before make sure your server is running as well. You can open multiple browser tabs http://localhost:5173/
and see that messages are updated in real-time on each update.
Message Broadcasting
Message broadcasting is an important concept of Socket.io. The difference between socket.emit()
, io.emit()
and io.on()
, socket.on()
revolves around the scope of the message broadcasting.
io.on()
listens for events on the global io
object, primarily for handling new connections. This is used to handle broader server-level events.
socket.on()
listens for events on an individual socket
connection. This is used within the context of a specific client connected to the server.
io.emit()
sends a message to all clients connected to the server. This is used when you want to broadcast a message to all connected clients simultaneously.
socket.emit()
sends a message only to the specific client connected through that socket
. This is useful when you want to communicate directly with a single client.
Summary
This tutorial taught us how to build a simple real-time chat application using Socket.io, React, and Express.js. We covered setting up a server with Express and Socket.io, creating a client with React, and the fundamentals of message broadcasting.
If you found this tutorial helpful, feel free to share it with others who might benefit from it! And as always, leave comments or questions below if you need further assistance or have suggestions!