Skip to content

Commit 2324567

Browse files
1 parent 9fbccd6 commit 2324567

File tree

2 files changed

+301
-0
lines changed

2 files changed

+301
-0
lines changed

src/pages/get-started/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ If you are new to Socket.IO, we recommend checking out our [tutorial](/docs/v4/t
2424

2525
- [React](/how-to/use-with-react)
2626
- [Vue 3](/how-to/use-with-vue)
27+
- [Next.js](/how-to/use-with-nextjs)
2728

2829
## How to...
2930

src/pages/how-to/use-with-nextjs.md

+300
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
---
2+
title: How to use with Next.js
3+
---
4+
5+
# How to use with Next.js
6+
7+
This guide shows how to use Socket.IO within a [Next.js](https://nextjs.org/) application.
8+
9+
## Server
10+
11+
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:
12+
13+
```js title=server.js
14+
import { createServer } from "node:http";
15+
import next from "next";
16+
import { Server } from "socket.io";
17+
18+
const dev = process.env.NODE_ENV !== "production";
19+
const hostname = "localhost";
20+
const port = 3000;
21+
// when using middleware `hostname` and `port` must be provided below
22+
const app = next({ dev, hostname, port });
23+
const handler = app.getRequestHandler();
24+
25+
app.prepare().then(() => {
26+
const httpServer = createServer(handler);
27+
28+
const io = new Server(httpServer);
29+
30+
io.on("connection", (socket) => {
31+
// ...
32+
});
33+
34+
httpServer
35+
.once("error", (err) => {
36+
console.error(err);
37+
process.exit(1);
38+
})
39+
.listen(port, () => {
40+
console.log(`> Ready on http://${hostname}:${port}`);
41+
});
42+
});
43+
```
44+
45+
The `server.js` file becomes the entrypoint of your application:
46+
47+
```diff package.json
48+
{
49+
"scripts": {
50+
- "dev": "next dev",
51+
+ "dev": "node server.js",
52+
"build": "next build",
53+
- "start": "next start",
54+
+ "start": "NODE_ENV=production node server.js",
55+
"lint": "next lint"
56+
}
57+
}
58+
```
59+
60+
And voilà!
61+
62+
Reference: https://nextjs.org/docs/pages/building-your-application/configuring/custom-server
63+
64+
:::tip
65+
66+
This works with both the App router and the Pages router.
67+
68+
:::
69+
70+
:::caution
71+
72+
From the Next.js documentation:
73+
74+
- 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](https://nextjs.org/docs/pages/building-your-application/rendering/automatic-static-optimization).
75+
- A custom server cannot be deployed on [Vercel](https://vercel.com/solutions/nextjs).
76+
- Standalone output mode, does not trace custom server files and this mode outputs a separate minimal server.js file instead.
77+
78+
:::
79+
80+
## Client
81+
82+
On the client side, all tips from our [React guide](/how-to/use-with-react) are valid.
83+
84+
The only difference is that you need to exclude the Socket.IO client from server-side rendering (SSR):
85+
86+
import Tabs from '@theme/Tabs';
87+
import TabItem from '@theme/TabItem';
88+
89+
<Tabs groupId="nextjs-router">
90+
<TabItem value="app" label="App router" default>
91+
92+
Structure:
93+
94+
```
95+
├── src
96+
│ ├── app
97+
│ │ └── page.js
98+
│ └── socket.js
99+
└── package.json
100+
```
101+
102+
```js title="src/socket.js"
103+
"use client";
104+
105+
import { io } from "socket.io-client";
106+
107+
export const socket = io();
108+
```
109+
110+
:::note
111+
112+
`"use client"` indicates that the file is part of the client bundle, and won't be server-rendered.
113+
114+
Reference: https://nextjs.org/docs/app/building-your-application/rendering/client-components
115+
116+
:::
117+
118+
```js title="src/app/page.js"
119+
"use client";
120+
121+
import { useEffect, useState } from "react";
122+
import { socket } from "../socket";
123+
124+
export default function Home() {
125+
const [isConnected, setIsConnected] = useState(false);
126+
const [transport, setTransport] = useState("N/A");
127+
128+
useEffect(() => {
129+
if (socket.connected) {
130+
onConnect();
131+
}
132+
133+
function onConnect() {
134+
setIsConnected(true);
135+
setTransport(socket.io.engine.transport.name);
136+
137+
socket.io.engine.on("upgrade", (transport) => {
138+
setTransport(transport.name);
139+
});
140+
}
141+
142+
function onDisconnect() {
143+
setIsConnected(false);
144+
setTransport("N/A");
145+
}
146+
147+
socket.on("connect", onConnect);
148+
socket.on("disconnect", onDisconnect);
149+
150+
return () => {
151+
socket.off("connect", onConnect);
152+
socket.off("disconnect", onDisconnect);
153+
};
154+
}, []);
155+
156+
return (
157+
<div>
158+
<p>Status: { isConnected ? "connected" : "disconnected" }</p>
159+
<p>Transport: { transport }</p>
160+
</div>
161+
);
162+
}
163+
```
164+
165+
</TabItem>
166+
<TabItem value="pages" label="Pages router">
167+
168+
Structure:
169+
170+
```
171+
├── src
172+
│ ├── pages
173+
│ │ └── index.js
174+
│ └── socket.js
175+
└── package.json
176+
```
177+
178+
```js title="src/socket.js"
179+
import { io } from "socket.io-client";
180+
181+
const isBrowser = typeof window !== "undefined";
182+
183+
export const socket = isBrowser ? io() : {};
184+
```
185+
186+
:::note
187+
188+
The `isBrowser` check is important, as it prevents Next.js from trying to create a Socket.IO client when doing server-side rendering.
189+
190+
Reference: https://nextjs.org/docs/pages/building-your-application/rendering/client-side-rendering
191+
192+
:::
193+
194+
```js title="src/pages/index.js"
195+
import { useEffect, useState } from "react";
196+
import { socket } from "../socket";
197+
198+
export default function Home() {
199+
const [isConnected, setIsConnected] = useState(false);
200+
const [transport, setTransport] = useState("N/A");
201+
202+
useEffect(() => {
203+
if (socket.connected) {
204+
onConnect();
205+
}
206+
207+
function onConnect() {
208+
setIsConnected(true);
209+
setTransport(socket.io.engine.transport.name);
210+
211+
socket.io.engine.on("upgrade", (transport) => {
212+
setTransport(transport.name);
213+
});
214+
}
215+
216+
function onDisconnect() {
217+
setIsConnected(false);
218+
setTransport("N/A");
219+
}
220+
221+
socket.on("connect", onConnect);
222+
socket.on("disconnect", onDisconnect);
223+
224+
return () => {
225+
socket.off("connect", onConnect);
226+
socket.off("disconnect", onDisconnect);
227+
};
228+
}, []);
229+
230+
return (
231+
<div>
232+
<p>Status: { isConnected ? "connected" : "disconnected" }</p>
233+
<p>Transport: { transport }</p>
234+
</div>
235+
);
236+
}
237+
```
238+
239+
</TabItem>
240+
</Tabs>
241+
242+
:::note
243+
244+
We could have used:
245+
246+
```js
247+
const [isConnected, setIsConnected] = useState(socket.connected);
248+
```
249+
250+
instead of:
251+
252+
```js
253+
const [isConnected, setIsConnected] = useState(false);
254+
255+
useEffect(() => {
256+
if (socket.connected) {
257+
onConnect();
258+
}
259+
// ...
260+
});
261+
```
262+
263+
but this triggers some warnings from the Next.js compiler, as the client-rendered page may not match the server-rendered output.
264+
265+
> Uncaught Error: Text content does not match server-rendered HTML.
266+
267+
:::
268+
269+
In the example above, the `transport` variable is the low-level transport used to establish the Socket.IO connection, which can be either:
270+
271+
- HTTP long-polling (`"polling"`)
272+
- [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) (`"websocket"`)
273+
- [WebTransport](https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API) (`"webtransport"`)
274+
275+
If everything went well, you should see:
276+
277+
```
278+
Status: connected
279+
Transport: websocket
280+
```
281+
282+
You can then exchange messages between the Socket.IO server and client with:
283+
284+
- `socket.emit()` to send messages
285+
286+
```js
287+
socket.emit("hello", "world");
288+
```
289+
290+
- `socket.on()` to receive messages
291+
292+
```js
293+
socket.on("hello", (value) => {
294+
// ...
295+
});
296+
```
297+
298+
That's all folks, thanks for reading!
299+
300+
[Back to the list of examples](/get-started/)

0 commit comments

Comments
 (0)