Open In App

Real-time Notification System using Next.js and Socket.io

Last Updated : 19 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

This article explains the process of creating a Real Time Notification System using Next.js and socket.io. In a typical scenario, imagine the need to create a user-friendly application. This application helps the user to send and receive notifications and can be used in multiple projects like E-commerce, chat applications, etc.

Output Preview: Let us have a look at how the final output will look like.

Screenshot-from-2024-03-05-02-39-19

Prerequisite:

Approach to create a Real-time Notification:

  • It receives real-time updates via a WebSocket connection using the socket.io-client library.
  • app/page.js is a page component that uses the socket.io-client library to connect to the server and listen for notifications. When a notification is received, it is displayed on the page.
  • app/layout.js is a wrapper for all pages in the app. It’s a good place to put shared components and styles.
  • app/add/page.js is the page component for the add route. It is a React component that is rendered when the user navigates to /add. It contains a form that allows the user to add a new notification. When the form is submitted, the handleSubmit function is called, which sends a POST request to the server with the notification data.
  • Establishes a WebSocket connection using io(‘http://localhost:4000’) via useMemo.
  • Uses useEffect to handle connection events and attempts to reconnect in case of errors.
  • Creates an Express server and a Socket.io server.
  • server.js is the file that will handle the POST request from the client and emit the notification to the client.
  • Handles WebSocket connections and updates in real-time.
  • Logs user connections, emits initial data, and updates data on send notification.

Steps to create the NextJS Application

Step 1: Create a Next.js project using the following command:

npx create-next-app RealTime
cd RealTime

Step 2: Create a file server.js inside root directory.

Step 3: Install the required dependency in your server using the following command.

npm i express cors socket.io socket.io-client

Project Structure:

Screenshot-from-2024-03-05-02-33-14

Project Structure of Real Time Notification System

The updated dependencies in package.json file of backend will look like:

"dependencies": {
"axios": "^1.6.7",
"axos": "^0.0.1",
"cors": "^2.8.5",
"express": "^4.18.3",
"next": "14.1.1",
"react": "^18",
"react-dom": "^18",
"socket.io": "^4.7.4",
"socket.io-client": "^4.7.4"
},
"devDependencies": {
"autoprefixer": "^10.0.1",
"postcss": "^8",
"tailwindcss": "^3.3.0"
}

Example: Add the following codesin the respective files.

Javascript
//server.js

const express = require("express");
const app = express();
const PORT = 4000;

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

//New imports
const http = require("http").Server(app);
const cors = require("cors");

app.use(cors());

const socketIO = require('socket.io')(http, {
    cors: {
        origin: "*"
    }
});

//Add this before the app.get() block
socketIO.on('connection', (socket) => {
    console.log(`âš¡: ${socket.id} user just connected!`);

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

app.post("/api", (req, res) => {
    const { name, message } = req.body;
    socketIO.emit('notification', { name, message });
    console.log(name, message);

    res.status(200).json({ name, message });
});

http.listen(PORT, () => {
    console.log(`Server listening on ${PORT}`);
});
Javascript
// app/page.js

"use client"
import { useEffect, useMemo, useState } from "react";
import socketio from "socket.io-client";

export default function Home() {

    const socket = socketio.connect("http://localhost:4000")

    useEffect(() => {
        socket.on('connect', () => {
            console.log(`Connected to server`);
        })

        socket.on('notification', (data) => {
            console.log(`Notification from server`);
            setNotifications([...notifications, data])
        })

        socket.on('disconnect', () => {
            console.log(`Disconnected from server`);
        })
    }, [socket])

    const [notifications, setNotifications] = useState([]);

    return (
        <main className="grid grid-cols-2 p-24 gap-6">

            {
                notifications ? notifications.map((notification, index) => {
                    return (
                        <div key={index} id="toast-message-cta"
                            className="w-full max-w-xs p-4 text-gray-500 
                        bg-white rounded-lg shadow dark:bg-gray-800 
                        dark:text-gray-400" role="alert">
                            <div className="flex">
                                <img className="w-8 h-8 rounded-full"
                                    src="notification.png" alt="Jese Leos image" />
                                <div className="ms-3 text-sm font-normal">
                                    <span className="mb-1 text-sm font-semibold
                                 text-gray-900 dark:text-white">
                                        {notification.name}</span>
                                    <div className="mb-2 text-sm font-normal">
                                        {notification.message}</div>
                                    <a href="#" className="inline-flex px-2.5
                                 py-1.5 text-xs font-medium text-center 
                                 text-white bg-blue-600 rounded-lg hover:bg-blue-700
                                  focus:ring-4 focus:outline-none focus:ring-blue-300
                                   dark:bg-blue-500 dark:hover:bg-blue-600 
                                   dark:focus:ring-blue-800">Reply</a>
                                </div>
                                <button type="button" className="ms-auto -mx-1.5
                             -my-1.5 bg-white justify-center items-center
                              flex-shrink-0 text-gray-400 hover:text-gray-900
                               rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5
                                hover:bg-gray-100 inline-flex h-8 w-8 
                                dark:text-gray-500 dark:hover:text-white dark:bg-gray-800
                                 dark:hover:bg-gray-700"
                                    data-dismiss-target="#toast-message-cta"
                                    aria-label="Close">
                                    <span className="sr-only">Close</span>
                                    <svg className="w-3 h-3" aria-hidden="true"
                                        xmlns="http://www.w3.org/2000/svg"
                                        fill="none" viewBox="0 0 14 14">
                                        <path stroke="currentColor"
                                            strokeLinecap="round" strokeLinejoin="round"
                                            strokeWidth="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
                                    </svg>
                                </button>
                            </div>
                        </div>
                    )
                }) : null

            }
        </main>
    );
}
Javascript
// app/layout.js

import Link from "next/link";
import "./globals.css";

export const metadata = {
    title: "GFG Notifications",
    description: "GFG Notifications - Send and receive notifications",
};

export default function RootLayout({ children }) {


    return (
        <html lang="en">
            <body className="min-h-screen">

                <nav className="bg-gray-200 shadow shadow-gray-300
                 w-100 px-8 md:px-auto">
                    <div className="md:h-16 h-28 mx-auto md:px-4
                     container flex items-center justify-between
                      flex-wrap md:flex-nowrap">
                        {/* Logo */}
                        <div className="text-indigo-500 md:order-1">
                            <img className="h-10" src="logo.png"
                                alt="Notifications" />
                        </div>
                        <div className="text-gray-500 order-3 
                        w-full md:w-auto md:order-2">
                            <ul className="flex font-semibold
                                           justify-between">
                                <li className="md:px-4 md:py-2 
                                text-green-500">
                                    <Link href="/">Home</Link>
                                </li>
                                <li className="md:px-4 md:py-2 
                                hover:text-green-400">
                                    <Link href="/add">
                                        Send Notification
                                    </Link>
                                </li>

                            </ul>
                        </div>
                        <div className="order-2 md:order-3">
                            <img className="h-6" 
                                 src="notification.png"
                                 alt="Notifications" />
                        </div>
                    </div>
                </nav>

                {children}

            </body>
        </html>
    );
}
Javascript
// app/add/page.js

"use client"
import axios from "axios";
import { useState } from "react";

export default function Add() {

    const handleSubmit = (e) => {
        e.preventDefault();

        const name = e.target[0].value;
        const message = e.target[1].value;

        axios.post("http://localhost:4000/api", { name, message })
            .then((res) => {
                console.log(res)
            })

        console.log("submitted", name, message)
    }


    return (
        <main className="flex p-10 justify-center gap-6">
            <div className="w-full max-w-xs">

                <h2 className="text-center my-5 text-2xl">
                    Send New Notification
                </h2>
                <form className="bg-white shadow-md 
                                 rounded px-8 pt-6 
                                 pb-8 mb-4"
                    onSubmit={handleSubmit}>
                    <div className="mb-4">
                        <label
                            className="block text-gray-700 
                                       text-sm font-bold mb-2"
                            htmlFor="username"
                        >
                            Notification Title
                        </label>
                        <input
                            className="shadow appearance-none 
                                       border rounded
                                        w-full py-2 px-3 
                                       text-gray-700 leading-tight 
                                        focus:outline-none 
                                       focus:shadow-outline"
                            type="text"
                            placeholder="Title"
                        />
                    </div>
                    <div className="mb-6">
                        <label
                            className="block text-gray-700 
                                       text-sm font-bold mb-2"
                        >
                            Notification Message
                        </label>
                        <input
                            className="shadow appearance-none 
                                       border rounded w-full
                                        py-2 px-3 text-gray-700 
                                       leading-tight 
                                       focus:outline-none
                                         focus:shadow-outline"
                            type="test"
                            placeholder="Message"
                        />

                    </div>
                    <div className="flex items-center 
                                    justify-between">
                        <button className="bg-blue-500 
                                              hover:bg-blue-700 
                                           text-white
                                               font-bold py-2 px-4 
                                              rounded 
                                           focus:outline-none
                                                focus:shadow-outline"
                            type="submit"
                        >
                            Send
                        </button>

                    </div>
                </form>
            </div>
        </main>
    );
}

Start your application using the following command.

node server.js  // for socket io server
npm run dev // for nextJS app

Output:

Screencast-from-2024-03-05-02-38-38

Real-time Notification System using Next.js and Socket.io



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads