DocsGuidesLive notifications

Live notifications

Live notifications: subscribe to a named topic and receive messages in real time from any other connected client; publish a message and every subscriber gets it immediately. Topics are ephemeral — messages are not stored and are delivered only to currently connected subscribers.

Push notifications

"User X is typing", "New alert!", live score updates.

Signaling

WebRTC offer/answer exchange, presence heartbeats, coordination.

Ephemeral events

Cursor positions, typing indicators, game state bursts.


Subscribe to a topic (React)

Use the useTopic hook. It manages the subscription lifecycle — subscribes on mount, unsubscribes on unmount. The hook returns a TopicHandle so you can publish from the same component.

components/ChatLog.tsx
import { useTopic } from '@topgunbuild/react';
import { useState } from 'react';

interface ChatMessage {
  userId: string;
  text: string;
}

export function ChatLog({ roomId }: { roomId: string }) {
  const [messages, setMessages] = useState<ChatMessage[]>([]);

  // Subscribe: callback fires on every incoming message
  useTopic(`chat:${roomId}`, (msg) => {
    setMessages(prev => [...prev, msg as ChatMessage]);
  });

  return (
    <ul>
      {messages.map((m, i) => (
        <li key={i}><strong>{m.userId}:</strong> {m.text}</li>
      ))}
    </ul>
  );
}

Publish to a topic (React)

useTopic returns a TopicHandle. Call .publish(payload) to send a message to all subscribers of that topic.

components/ChatInput.tsx
import { useTopic } from '@topgunbuild/react';
import { useState } from 'react';

interface ChatMessage {
  userId: string;
  text: string;
}

export function ChatInput({ roomId, userId }: { roomId: string; userId: string }) {
  const [text, setText] = useState('');

  // Subscribe and get a handle for publishing
  const topic = useTopic(`chat:${roomId}`);

  const send = () => {
    if (!text.trim()) return;
    topic.publish({ userId, text } satisfies ChatMessage);
    setText('');
  };

  return (
    <div>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        onKeyDown={(e) => e.key === 'Enter' && send()}
        placeholder="Type a message..."
      />
      <button onClick={send}>Send</button>
    </div>
  );
}

Non-React contexts

In Node.js services, service workers, or integration tests, use the imperative client.topic(name) API directly. The TopicHandle returned by client.topic() is the same object useTopic wraps.

src/server/notifications.ts
import { TopGunClient } from '@topgunbuild/client';
import { IDBAdapter } from '@topgunbuild/adapters';

const client = new TopGunClient({
  serverUrl: 'ws://localhost:8080',
  storage: new IDBAdapter(),
});

await client.start();

const chatTopic = client.topic('chat:room-general');

// Subscribe
const unsubscribe = chatTopic.subscribe((data, context) => {
  console.log('Received:', data);
  // context.publisherId — sender's nodeId (undefined if sent from server)
  // context.timestamp — HLC timestamp of the message
  console.log('From:', context.publisherId, 'At:', new Date(context.timestamp));
});

// Publish
chatTopic.publish({ userId: 'alice', text: 'Hello from Node.js!' });

// Stop listening when done
unsubscribe();

Topic ordering and delivery

PropertyBehaviour
Delivery guaranteeAt-most-once. Messages are not ACKed or persisted.
Offline behaviourIf a subscriber is offline when a message is published, they do not receive it on reconnect. Use getMap() or getORMap() for durable history.
Re-subscriptionThe client automatically re-subscribes to all active topics on reconnect to the server.
OrderingMessages from a single publisher arrive in send order. Cross-publisher ordering is not guaranteed.
Payload typeAny JSON-serialisable value: string, number, object, array.

When to use topics vs maps

Use topics for transient events where delivery to currently-offline users is not required (typing indicators, cursor positions, live scores). Use maps (via useQuery / getMap) for durable data that must survive disconnects and page reloads.


Next steps