Skip to main content

How to use with Next.js

This guide shows how to use Socket.IO within a Next.js application.

caution

You won't be able to deploy your application on Vercel, as it does not support WebSocket connections.

Reference: https://vercel.com/guides/do-vercel-serverless-functions-support-websocket-connections

Server​

The Socket.IO server can share the same underlying HTTP server with Next.js. You just have to create a server.js file at the root of your project:

server.js
import { createServer } from "node:http";
import next from "next";
import { Server } from "socket.io";

const dev = process.env.NODE_ENV !== "production";
const hostname = "localhost";
const port = 3000;
// when using middleware `hostname` and `port` must be provided below
const app = next({ dev, hostname, port });
const handler = app.getRequestHandler();

app.prepare().then(() => {
const httpServer = createServer(handler);

const io = new Server(httpServer);

io.on("connection", (socket) => {
// ...
});

httpServer
.once("error", (err) => {
console.error(err);
process.exit(1);
})
.listen(port, () => {
console.log(`> Ready on http://${hostname}:${port}`);
});
});

The server.js file becomes the entrypoint of your application:

{
"scripts": {
- "dev": "next dev",
+ "dev": "node server.js",
"build": "next build",
- "start": "next start",
+ "start": "NODE_ENV=production node server.js",
"lint": "next lint"
}
}

And voilà!

Reference: https://nextjs.org/docs/pages/building-your-application/configuring/custom-server

tip

This works with both the App router and the Pages router.

caution

From the Next.js documentation:

  • Before deciding to use a custom server, please keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like serverless functions and Automatic Static Optimization.
  • A custom server cannot be deployed on Vercel.
  • Standalone output mode, does not trace custom server files and this mode outputs a separate minimal server.js file instead.

Client​

On the client side, all tips from our React guide are valid.

The only difference is that you need to exclude the Socket.IO client from server-side rendering (SSR):

Structure:

├── src
│ ├── app
│ │ └── page.js
│ └── socket.js
└── package.json
src/socket.js
"use client";

import { io } from "socket.io-client";

export const socket = io();
note

"use client" indicates that the file is part of the client bundle, and won't be server-rendered.

Reference: https://nextjs.org/docs/app/building-your-application/rendering/client-components

src/app/page.js
"use client";

import { useEffect, useState } from "react";
import { socket } from "../socket";

export default function Home() {
const [isConnected, setIsConnected] = useState(false);
const [transport, setTransport] = useState("N/A");

useEffect(() => {
if (socket.connected) {
onConnect();
}

function onConnect() {
setIsConnected(true);
setTransport(socket.io.engine.transport.name);

socket.io.engine.on("upgrade", (transport) => {
setTransport(transport.name);
});
}

function onDisconnect() {
setIsConnected(false);
setTransport("N/A");
}

socket.on("connect", onConnect);
socket.on("disconnect", onDisconnect);

return () => {
socket.off("connect", onConnect);
socket.off("disconnect", onDisconnect);
};
}, []);

return (
<div>
<p>Status: { isConnected ? "connected" : "disconnected" }</p>
<p>Transport: { transport }</p>
</div>
);
}
note

We could have used:

const [isConnected, setIsConnected] = useState(socket.connected);

instead of:

const [isConnected, setIsConnected] = useState(false);

useEffect(() => {
if (socket.connected) {
onConnect();
}
// ...
});

but this triggers some warnings from the Next.js compiler, as the client-rendered page may not match the server-rendered output.

Uncaught Error: Text content does not match server-rendered HTML.

In the example above, the transport variable is the low-level transport used to establish the Socket.IO connection, which can be either:

If everything went well, you should see:

Status: connected
Transport: websocket

You can then exchange messages between the Socket.IO server and client with:

  • socket.emit() to send messages
socket.emit("hello", "world");
  • socket.on() to receive messages
socket.on("hello", (value) => {
// ...
});

That's all folks, thanks for reading!

Back to the list of examples