Broadcast events
Send typed, transient events to the other connections in a room.
Broadcasts deliver a one-off event to the connections that are in the room right now. Use them for reactions, notifications, presentation controls, or other signals that do not need to be persisted.
Define events
Map each event name to its payload:
type Events = {
reaction: {
emoji: string;
x: number;
y: number;
};
slideChanged: {
slideId: string;
};
};Pass the type to RoomProvider:
<RoomProvider<Presence, Storage, Events>
id="presentation:launch"
initialPresence={{ name: "Ada" }}
initialStorage={{ currentSlideId: "intro" }}
>
<Presentation />
</RoomProvider>Send an event
import { useBroadcast } from "@polynomialhq/react";
function Reactions() {
const broadcast = useBroadcast<Events>();
return (
<button
onClick={() => {
broadcast("reaction", {
emoji: "π",
x: 320,
y: 180,
});
}}
type="button"
>
Applaud
</button>
);
}TypeScript checks both the event name and its payload.
Listen for one event
import { useBroadcastEvent } from "@polynomialhq/react";
function ReactionLayer() {
useBroadcastEvent<Events, "reaction">("reaction", ({ connectionId, payload }) => {
showReaction({
...payload,
sender: connectionId,
});
});
return <div id="reaction-layer" />;
}The listener receives:
| Field | Description |
|---|---|
event | Typed event name |
payload | Typed payload for that event |
connectionId | Sending connection |
The sender receives its own broadcast
The realtime service fans an event out to every connection in the room, including the one that sent it. If your handler applies a visual effect, the sender will see it twice unless you filter:
const connection = useConnectionSnapshot();
useBroadcastEvent<Events, "reaction">("reaction", (event) => {
if (event.connectionId === connection.connectionId) {
return;
}
showReaction(event.payload);
});Alternatively, let the echo be your confirmation and apply the effect only when it arrives.
Pass a stable listener. useBroadcastEvent resubscribes whenever the listener identity changes, so
wrap it in useCallback if it closes over component state.
Framework-agnostic events
const { room } = client.enterRoom<Presence, Storage, Events>("presentation:launch", {
initialPresence,
initialStorage,
});
room.broadcast("slideChanged", { slideId: "results" });
room.subscribe((event) => {
if (event.type === "broadcast" && event.event.event === "slideChanged") {
navigateToSlide(event.event.payload.slideId);
}
});room.broadcastEvent(event) is a convenience form for objects that already carry a type field:
it sends event.type as the event name and the whole object as the payload.
Broadcast from a server
A trusted backend can send an event through the management API:
import { PolynomialServer } from "@polynomialhq/server";
const polynomial = new PolynomialServer({
secretKey: process.env.POLYNOMIAL_SECRET_KEY!,
});
await polynomial.broadcastEvent({
event: {
type: "deployment.finished",
deploymentId: "deploy_123",
},
projectId: "project_123",
roomId: "project-room:42",
});Server broadcasts are useful for job progress, webhook results, or administrative signals. The
event object must contain a type string; browser listeners receive it under that name.
When to use storage instead
Broadcasts are not retained or replayed. Use storage when:
- a user joining later needs the value;
- a reconnect must restore it;
- the event changes the durable document;
- you need undo and redo.
A broadcast sent while a client is reconnecting is lost for that client β there is no replay buffer. Anything that must survive a two-second network blip belongs in storage.