Real-time applications
Build chat applications, live dashboards, collaborative tools, and multiplayer experiences with CloudSignal's real-time messaging.
CloudSignal powers real-time features in web and mobile applications, from chat and notifications to live dashboards and collaborative editing. This page walks through the most common patterns and the topic shapes that fit them.
Use cases
Chat applications
Build instant messaging with message history, typing indicators, and presence:
Implementation
// Chat room component using the CloudSignal SDK
function ChatRoom({ roomId, userId }: { roomId: string; userId: string }) {
const [messages, setMessages] = useState<Message[]>([]);
const [typingUsers, setTypingUsers] = useState<Set<string>>(new Set());
const { client, isConnected } = useCloudSignal();
useEffect(() => {
if (!client || !isConnected) return;
client.subscribe(`chat/${roomId}/messages`);
client.subscribe(`chat/${roomId}/typing`);
client.onMessage((topic, data: any) => {
if (topic === `chat/${roomId}/messages`) {
setMessages((prev) => [...prev, data]);
} else if (topic === `chat/${roomId}/typing`) {
setTypingUsers((prev) => {
const next = new Set(prev);
if (data.typing) next.add(data.userId);
else next.delete(data.userId);
return next;
});
}
});
}, [client, isConnected, roomId]);
const sendMessage = (text: string) => {
client?.transmit(`chat/${roomId}/messages`, {
userId,
text,
timestamp: Date.now(),
});
};
const setTyping = (isTyping: boolean) => {
client?.transmit(`chat/${roomId}/typing`, {
userId,
typing: isTyping,
});
};
// ... render UI
}Live dashboards
Stream real-time data to dashboards:
// Dashboard receiving live metrics
function LiveDashboard() {
const [metrics, setMetrics] = useState<Metrics | null>(null);
const { client, isConnected } = useCloudSignal();
useEffect(() => {
if (!client || !isConnected) return;
client.subscribe('metrics/realtime');
client.onMessage((_topic, data: any) => {
setMetrics(data);
});
}, [client, isConnected]);
return (
<div className="grid grid-cols-3 gap-4">
<MetricCard title="Active users" value={metrics?.activeUsers} />
<MetricCard title="Requests/sec" value={metrics?.requestsPerSec} />
<MetricCard title="Latency" value={metrics?.avgLatency} unit="ms" />
</div>
);
}# Backend publishing metrics
import threading
import time
import json
def metrics_publisher():
while True:
metrics = collect_metrics()
client.publish(
'metrics/realtime',
json.dumps({
'activeUsers': metrics['active_users'],
'requestsPerSec': metrics['rps'],
'avgLatency': metrics['latency_ms'],
'timestamp': time.time(),
}),
)
time.sleep(1) # Update every second
threading.Thread(target=metrics_publisher, daemon=True).start()Notifications
Push notifications to users in real time:
// Client: receive notifications
function NotificationListener({ userId }: { userId: string }) {
const { client, isConnected } = useCloudSignal();
useEffect(() => {
if (!client || !isConnected) return;
client.subscribe(`notifications/${userId}`);
client.subscribe('notifications/broadcast');
client.onMessage((_topic, notification: any) => {
// Show browser notification
if (Notification.permission === 'granted') {
new Notification(notification.title, {
body: notification.body,
icon: notification.icon,
});
}
// Or update in-app notification center
addNotification(notification);
});
}, [client, isConnected, userId]);
return null;
}# Server: send notification
import json
import time
def send_notification(user_id: str, title: str, body: str):
client.publish(
f'notifications/{user_id}',
json.dumps({
'title': title,
'body': body,
'timestamp': time.time(),
}),
qos=1, # Ensure delivery
)
def broadcast_notification(title: str, body: str):
client.publish(
'notifications/broadcast',
json.dumps({
'title': title,
'body': body,
'timestamp': time.time(),
}),
)Collaborative editing
Sync document changes in real time:
// Collaborative document editor
function CollaborativeEditor({ docId, userId }: { docId: string; userId: string }) {
const [content, setContent] = useState('');
const [cursors, setCursors] = useState<Map<string, CursorPosition>>(new Map());
const { client, isConnected } = useCloudSignal();
useEffect(() => {
if (!client || !isConnected) return;
client.subscribe(`docs/${docId}/changes`);
client.subscribe(`docs/${docId}/cursors`);
client.onMessage((topic, data: any) => {
if (topic.endsWith('/changes') && data.userId !== userId) {
applyRemoteChange(data.operation);
} else if (topic.endsWith('/cursors') && data.userId !== userId) {
setCursors((prev) => new Map(prev).set(data.userId, data.position));
}
});
}, [client, isConnected, docId, userId]);
const handleChange = (operation: Operation) => {
setContent(applyOperation(content, operation));
client?.transmit(`docs/${docId}/changes`, {
userId,
operation,
timestamp: Date.now(),
});
};
const handleCursorMove = (position: CursorPosition) => {
client?.transmit(`docs/${docId}/cursors`, { userId, position });
};
// ... render editor with remote cursors
}Multiplayer experiences
Sync state between players:
// Game-state sync
function MultiplayerGame({ gameId, playerId }: { gameId: string; playerId: string }) {
const [players, setPlayers] = useState<Map<string, PlayerState>>(new Map());
const { client, isConnected } = useCloudSignal();
useEffect(() => {
if (!client || !isConnected) return;
client.subscribe(`games/${gameId}/state`);
client.subscribe(`games/${gameId}/events`);
client.onMessage((topic, data: any) => {
if (topic.endsWith('/state')) {
setPlayers((prev) => {
const next = new Map(prev);
next.set(data.playerId, data.state);
return next;
});
} else if (topic.endsWith('/events')) {
handleGameEvent(data);
}
});
}, [client, isConnected, gameId]);
// Send position updates at fixed interval
useInterval(() => {
const state = getPlayerState();
client?.transmit(`games/${gameId}/state`, {
playerId,
state,
timestamp: Date.now(),
}, { qos: 0 }); // QoS 0 for high-frequency updates
}, 50); // 20 updates per second
// ... render game
}Security for browser apps
Never expose permanent credentials in browser code. Mint short-lived tokens on your server.
import CloudSignal from '@cloudsignal/mqtt-client';
// 1. Request a token from your server (which holds the secret key)
const response = await fetch('/api/mqtt-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId }),
});
const { accessToken } = await response.json();
// 2. Connect with the short-lived token
const client = new CloudSignal({
preset: 'desktop',
clientId: `browser-${userId}-${Date.now()}`,
});
await client.connectWithToken({
organizationId: process.env.NEXT_PUBLIC_CLOUDSIGNAL_ORG_ID!,
externalToken: accessToken,
});See Server-side tokens for the full implementation.
Best practices
Topic design
# User-specific topics
notifications/{user_id}
chat/dm/{user_id_a}/{user_id_b}
# Room-based topics
chat/rooms/{room_id}/messages
chat/rooms/{room_id}/typing
chat/rooms/{room_id}/presence
# Broadcast topics
notifications/broadcast
system/statusMessage size
Keep messages small for optimal performance:
// Good: minimal payload
client.transmit('game/state', {
id: playerId,
x: position.x,
y: position.y,
t: Date.now(),
});
// Avoid: oversized payload
client.transmit('game/state', {
playerId,
position: { x: position.x, y: position.y, z: 0 },
velocity: { x: vel.x, y: vel.y, z: 0 },
rotation: { x: 0, y: 0, z: rotation },
health: 100,
inventory: [...items],
timestamp: new Date().toISOString(),
});Throttle high-frequency updates
// Throttle cursor position updates
const throttledCursorUpdate = useMemo(
() => throttle((position) => {
client?.transmit(`docs/${docId}/cursors`, { userId, position });
}, 50), // Max 20 updates per second
[client, docId, userId],
);Connection management
Handle reconnection gracefully:
const client = new CloudSignal({
preset: 'desktop',
reconnectPeriod: 5000, // Reconnect after 5s
connectTimeout: 30000, // Time out after 30s
});
client.on('reconnect', () => {
console.log('Reconnecting...');
});
client.on('connect', () => {
// Re-subscribe to topics after reconnect
resubscribe();
});Related
- Next.js quickstart - Full React implementation
- Server-side tokens - Secure browser authentication