WebSocket

Push real-time updates and notifications from Hono routes to the browser over a multiplexed, cookie-authenticated WebSocket connection.

VitNode provides a unified, multiplexed WebSocket connection at /api/ws. Browsers share a single connection across tabs via a Web Lock leader election, authenticated by session cookies.

Quick start

1. Send User Notification

Push real-time notifications to any signed-in user from any Hono route or service:

import { notificationsChannel } from "@vitnode/core/ws/notifications"

c.get("realtime").sendToUser(userId, notificationsChannel, {
  title: "New Comment",
  description: "Alex commented on your post.",
  type: "info",
})

The user receives an immediate sonner toast in all open tabs across devices.


2. Broadcast to Everyone

Broadcast live updates to all connected visitors:

c.get("realtime").broadcast(myChannel, {
  count: 142,
})

Define a Custom Channel

Create typed channels in your plugin:

plugins/chat/src/ws/chat.channel.ts
import { defineWebSocketChannel } from "@vitnode/core/ws"
import { z } from "zod"

export interface MessagePayload {
  roomId: string
  message: string
  senderId: number
}

export const chatChannel = defineWebSocketChannel<MessagePayload>({
  id: "chat_room_messages",
  schema: z.object({
    roomId: z.string(),
    message: z.string(),
    senderId: z.number(),
  }),
})

Client-Side Consumption

Listen for incoming channel messages with useWebSocketChannel:

plugins/chat/src/views/chat-room.tsx
import { useWebSocketChannel } from "@vitnode/core/hooks/use-websocket-channel"
import { chatChannel } from "../ws/chat.channel"

export const ChatRoom = ({ roomId }: { roomId: string }) => {
  const [messages, setMessages] = React.useState<string[]>([])

  useWebSocketChannel(chatChannel, (payload) => {
    if (payload.roomId === roomId) {
      setMessages((prev) => [...prev, payload.message])
    }
  })

  return (
    <div>
      {messages.map((msg, i) => (
        <p key={i}>{msg}</p>
      ))}
    </div>
  )
}

Architectural Highlights

  • Single Connection: Only one /api/ws socket is opened per client. All features share it via message multiplexing.
  • Tab Leader Election: When a user opens multiple tabs, a Web Lock elects one leader tab to hold the socket, distributing messages across tabs via BroadcastChannel.
  • Automatic Reconnect: Backoff retry automatically re-establishes dropped connections within seconds.

Learn More