Export a socket() function from any page for bidirectional real-time communication via Socket.IO. Unlike subscribe() (server-to-client only), socket() enables both sending and receiving events. See live demo →
lumenjs add socketio
This installs socket.io and socket.io-client, and adds 'socketio' to your config integrations.
// pages/chat/[room].ts export function socket({ on, push, room, params }) { room.join(params.room); on('message', (msg) => { room.broadcast(params.room, { user: msg.user, text: msg.text }); }); return () => room.leave(params.room); } export class ChatRoom extends LitElement { static properties = { user: { type: String }, text: { type: String }, }; user = ''; text = ''; sendMessage(text) { this.emit('message', { user: 'me', text }); } render() { return html`<p>${this.text}</p>`; } }
loader() and subscribe(), the socket() function runs only on the server and is stripped from the client bundle. You can safely use database connections, Node.js APIs, and secrets inside it.
1. User navigates to the page. The framework connects via Socket.IO to a route-specific namespace
2. The server calls socket(). Your function sets up event listeners and room membership
3. Client sends events via this.emit(event, data) on the page component
4. Server receives events via on(event, handler) and can push data back or broadcast to rooms
5. Server pushes data via push(data). Each key is spread as a property on the component
6. User navigates away. Socket disconnects, cleanup function runs
The socket function receives a context object with:
| Property | Type | Description |
|---|---|---|
params | Record<string, string> | Dynamic route parameters |
headers | Record<string, any> | Connection headers |
locale | string? | Current locale (when i18n is configured) |
on | (event, handler) => void | Listen for client events |
push | (data) => void | Send data to this client (each key spread as a property) |
room.join | (name) => void | Join a room |
room.leave | (name) => void | Leave a room |
room.broadcast | (name, data) => void | Send to room (except sender) |
room.broadcastAll | (name, data) => void | Send to room (including sender) |
socket | Socket | Raw Socket.IO socket for advanced use |
When a page has a socket() export, the framework injects an emit() method on the page component:
// Inside your page component this.emit('message', { text: 'hello' }); this.emit('typing', { user: 'me' });
emit() method is only available on pages that export a socket() function. It sends events to the server handler's on() listeners.
Use rooms for broadcasting to groups of connected clients:
export function socket({ on, push, room, params }) { // Join a room based on route params room.join(params.room); on('message', (msg) => { // Send to everyone in the room except sender room.broadcast(params.room, msg); }); on('announcement', (msg) => { // Send to everyone in the room including sender room.broadcastAll(params.room, msg); }); return () => room.leave(params.room); }
Always return a cleanup function. It runs when the client disconnects (navigates away, closes tab, or loses connection).
_socket.ts)When the socket handler grows large, move it into a _socket.ts file alongside the page. For folder routes (index.ts pages), LumenJS discovers it automatically — no import or wrapper needed in the page file.
pages/
└── chat/
├── index.ts ← page component only
└── _socket.ts ← socket handler, auto-discovered
// pages/chat/_socket.ts export function socket({ on, push, room, params }) { room.join(params.room); on('message', (msg) => { room.broadcast(params.room, { user: msg.user, text: msg.text }); }); return () => room.leave(params.room); }
// pages/chat/index.ts — no socket() here at all export class ChatRoom extends LitElement { static properties = { user: { type: String }, text: { type: String } }; user = ''; text = ''; sendMessage(text) { this.emit('message', { user: 'me', text }); } render() { return html`<p>${this.text}</p>`; } }
socket(), it takes precedence. Co-located _socket.ts only applies to folder routes — flat pages like pages/chat.ts keep the socket handler inline.
Use subscribe() for simple server-to-client push (dashboard metrics, notifications, live feeds). It uses native SSE. Zero dependencies.
Use socket() when you need bidirectional communication (chat, collaborative editing, games, interactive features). It uses Socket.IO.
1. Must be a named export function. Not an arrow function, not a default export
2. Stripped from client bundles automatically
3. Each key from push() is spread as a property (same as subscribe() and loader())
4. Client sends events via this.emit(event, data)
5. Requires the socketio integration. Run lumenjs add socketio
6. Socket.IO client is only loaded when a page has socket(). Zero overhead otherwise