Building Real-Time Views Component: A Socket.IO and Next.js Tutorial

thumbnail

Hello Guys, Today in this post we will build a simple real-time views component in NEXT JS powered by Socket.IO and Node JS. Socket programming facilitates real-time communication between a server and clients. By the end, you’ll have a functional real-time view and a clear understanding of how Socket.IO and Next.js work together.

Prerequisites

Make sure you have the following installed on your machine:

Setting Up the Server

Create a new Node.js project and install the necessary packages, I will be using typescript.

mkdir realtime-view-server
cd realtime-view-server
npm init -y
npm install -D @types/node ts-node typescript
npm install express socket.io cors

Let’s code step by step:

  1. Import Dependencies:

    import { createServer } from 'http' import { Server } from 'socket.io'

Here, the code imports the necessary modules for creating an HTTP server (http), and setting up a Socket.IO server (socket.io).

2. Initialize Variables:

...
let count = 0 
let httpServer = createServer() 
const io = new Server(httpServer, {     
cors: {origin: 'http://localhost:3000'} })

The variable count is initialized to keep track of the number of connected clients. HTTP server is created using Node.js’s http module. This server is then passed to the Server constructor of Socket.IO, creating a WebSocket server (io). The cors configuration ensures that the server allows connections from http://localhost:3000.

3. Handle Connection Events:

...
io.on('connection', (socket) => {
    count++;
    console.log("connected: ", count)
    socket.on('disconnect', () => {
        count--
        console.log("disconnected: ", count)
        socket.broadcast.emit("count", count)
    })
    socket.emit("count", count)
    socket.broadcast.emit("count", count)
})

The io.on('connection', ...) block listens for incoming socket connections. When a client connects ('connection' event), the server increments the count variable, emits the count to the client also broadcasts the count to other clients connected, and sets up event listeners for disconnection. On disconnection ('disconnect' event), the server decrements the count, and broadcasts the count update to all connected clients except the disconnected one (socket.broadcast.emit).

...
httpServer.listen(3001)
console.log("listening port 3001")

The HTTP server is configured to listen on port 3001.

Final code for the server

import { createServer } from 'http'
import { Server } from 'socket.io'
let count = 0

let httpServer = createServer()
const io = new Server(httpServer, {
    cors: {
        origin: 'http://localhost:3000'
    }
})

io.on('connection', (socket) => {
    count++;
    console.log("connected: ", count)
    socket.on('disconnect', () => {
        count--
        console.log("disconnected: ", count)
        socket.emit("count", count)
        socket.broadcast.emit("count", count)
    })
    socket.emit("count", count)
    socket.broadcast.emit("count", count)
})


httpServer.listen(3001)
console.log("listening port 3001")

In the package.json configure a start command.

//package.json
{
  "name": "realtime-view-server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "ts-node index"
  },
  "author": "codewithmarish",
  "license": "ISC",
  "dependencies": {
    "socket.io": "^4.7.2"
  },
  "devDependencies": {
    "@types/node": "^20.10.0",
    "ts-node": "^10.9.1",
    "typescript": "^5.3.2"
  }
}

Now you can start your server using npm start from the command line.

Setting Up the Next.js Client

Create a new Next.js app using below:

npx create-next-app@latest

Install the socket.io-client package:

npm install socket.io-client

Now, create a RealtimeView component (components/RealtimeView.tsx):

// /components/RealtimeView.tsx
"use client"

import React, { useEffect, useState } from 'react'
import io from 'socket.io-client'
const API_URL = 'http://localhost:3001/'
const socket = io(API_URL, { autoConnect: false })

const RealtimeView = () => {
  const [views, setViews] = useState(0)
  useEffect(() => {
    socket.connect()
    socket.on("count", (count) => {
      setViews(count)
    })
    return () => {
      socket.disconnect()
    }
  }, [])
  return (
    <div>
      <h2 className='text-[100px] md:text-[200px] lg:text-[300px] flex items-center text-center font-extrabold'>
        <span className="relative flex h-5 w-5 mr-4">
          <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
          <span className="relative inline-flex rounded-full h-5 w-5 bg-green-500"></span>
        </span>
        <span className='leading-[80px] md:leading-[150px] lg:leading-[220px]'>{views}<span className='block text-base md:text-2xl lg:text-4xl'>Online</span></span></h2>
    </div >
  )
}
export default RealtimeView

First let's import the necessary modules for building the component, and managing side effects in functional components (useEffect and useState), and establishing a client-side socket connection using socket.io-client. The API_URL variable holds the URL for the Socket.IO server. The socket object is created using io from, with the autoConnect option set to false as we will establish a connection manually. The RealtimeView component component utilizes the state hook (useState) to manage the views state, representing the count of connected users. When the component mounts, it establishes a connection to the socket server (socket.connect()) and listens for the "count" event. The count received from the server updates the component state (setViews(count)). The useEffect also includes a cleanup function (return () => { socket.disconnect(); }) to disconnect the socket when the component is unmounted. The component’s render function returns JSX, displaying the current count (views). I have used ping animation from the below Tailwind docs

Animation - Tailwind CSS

Integrating the Component

Now, let’s use the RealtimeView component in the app/pages.tsx file:

// app/pages.tsx
import RealtimeView from '@/components/RealtimeView';

export default function Home() {
  return (
    <main className="min-h-screen flex justify-center items-center">
      <RealtimeView/>  
    </main>
  )
}

Now you can run the Next JS app using npm run devVisit http://localhost:3000 in your browser to see the Realtime View in action. You can open one in normal mode and another in incognito or a different browser. As users connect and disconnect, the view count updates in real time.

Congratulations! You’ve successfully created a Realtime View component using Socket.IO, Node.js, and Next.js. Feel free to explore and expand upon this foundation for more complex real-time features in your projects.