Skip to main content
Features

Realtime

Broadcast typed realtime events from Rivet Actors and subscribe from clients to receive state changes without polling or external pub-sub.

Events can be sent to clients connected using .connect(). They have no effect on low-level WebSocket connections.

Publishing Events from Actors

Broadcasting to All Clients

Define event names and payload types with events and event<T>(), then use c.broadcast(eventName, ...args) to send events to all connected clients:

import { actor, event } from "rivetkit";

type Message = {
  id: string;
  userId: string;
  text: string;
  timestamp: number;
};

const chatRoom = actor({
  state: {
    messages: [] as Message[]
  },

  events: {
    messageReceived: event<Message>()
  },

  actions: {
    sendMessage: (c, userId: string, text: string) => {
      const message = {
        id: crypto.randomUUID(),
        userId,
        text,
        timestamp: Date.now()
      };

      c.state.messages.push(message);

      // Broadcast to all connected clients
      c.broadcast('messageReceived', message);

      return message;
    },
  }
});

Sending to Specific Connections

Send events to individual connections using conn.send(eventName, ...args):

import { actor, event } from "rivetkit";

interface ConnState {
  playerId: string;
  role: string;
}

const gameRoom = actor({
  state: {
    players: {} as Record<string, {health: number, position: {x: number, y: number}}>
  },

  events: {
    privateMessage: event<{
      from?: string;
      message: string;
      timestamp: number;
    }>()
  },

  createConnState: (c, params: { playerId: string, role?: string }): ConnState => ({
    playerId: params.playerId,
    role: params.role || "player"
  }),

  actions: {
    sendPrivateMessage: (c, targetPlayerId: string, message: string) => {
      // Find the target player's connection
      let targetConn = null;
      for (const conn of c.conns.values()) {
        if (conn.state.playerId === targetPlayerId) {
          targetConn = conn;
          break;
        }
      }

      if (targetConn) {
        targetConn.send('privateMessage', {
          from: c.conn?.state.playerId,
          message,
          timestamp: Date.now()
        });
      } else {
        throw new Error("Player not found or not connected");
      }
    }
  }
});

Send events to all connections except the sender:

import { actor, event } from "rivetkit";

interface ConnState {
  playerId: string;
  role: string;
}

const gameRoom = actor({
  state: {
    players: {} as Record<string, {health: number, position: {x: number, y: number}}>
  },

  events: {
    playerMoved: event<{
      playerId: string;
      position: { x: number; y: number };
    }>()
  },

  createConnState: (c, params: { playerId: string, role?: string }): ConnState => ({
    playerId: params.playerId,
    role: params.role || "player"
  }),

  actions: {
    updatePlayerPosition: (c, position: {x: number, y: number}) => {
      const playerId = c.conn?.state.playerId;
      if (!playerId) return;

      if (c.state.players[playerId]) {
        c.state.players[playerId].position = position;

        // Send position update to all OTHER players
        for (const conn of c.conns.values()) {
          if (conn.state.playerId !== playerId) {
            conn.send('playerMoved', { playerId, position });
          }
        }
      }
    }
  }
});

Subscribing to Events from Clients

Clients must establish a connection to receive events from actors. Use .connect() to create a persistent connection, then listen for events.

Basic Event Subscription

Use connection.on(eventName, callback) to listen for events:

One-time Event Listeners

Use connection.once(eventName, callback) for events that should only trigger once:

Removing Event Listeners

Use the callback returned from .on() to remove event listeners:

Debugging

  • GET /inspector/connections shows active connections and connection metadata.
  • Use this to confirm clients are connected before expecting broadcasts.
  • GET /inspector/summary provides connections, RPCs, and queue size in one response.
  • In non-dev mode, inspector endpoints require authorization.

More About Connections

For more details on actor connections, including connection lifecycle, authentication, and advanced connection patterns, see the Connections documentation.