Realtime Subscriptions
PocketBase includes realtime out of the box: your app can subscribe to a collection (or a single record) and receive create, update, and delete events the moment they happen. Under the hood it uses Server-Sent Events (SSE), which works through PocketBase Cloud’s HTTPS proxy with no extra setup.
Subscribe to a collection
// listen to all changes in the "messages" collection
pb.collection("messages").subscribe("*", (e) => {
console.log(e.action); // "create" | "update" | "delete"
console.log(e.record); // the affected record
});
A typical chat or feed UI applies the event directly to local state:
pb.collection("messages").subscribe("*", (e) => {
if (e.action === "create") {
messages.push(e.record);
} else if (e.action === "update") {
const i = messages.findIndex((m) => m.id === e.record.id);
if (i >= 0) messages[i] = e.record;
} else if (e.action === "delete") {
messages = messages.filter((m) => m.id !== e.record.id);
}
});
Subscribe to a single record
pb.collection("orders").subscribe("ORDER_ID", (e) => {
updateStatusBadge(e.record.status);
});
Unsubscribe
Always clean up subscriptions when a component unmounts:
// remove a specific subscription
pb.collection("messages").unsubscribe("*");
// or remove all subscriptions on the client
pb.realtime.unsubscribe();
In React, pair subscribe/unsubscribe inside useEffect:
useEffect(() => {
pb.collection("messages").subscribe("*", handleEvent);
return () => pb.collection("messages").unsubscribe("*");
}, []);
Realtime respects your API rules
Events are only delivered for records the connected client is allowed to view according to the collection’s API rules. If a user can’t read a record via the REST API, they won’t receive realtime events for it either — so your ownership rules keep working with no extra code.
Subscriptions also support filters and expand options:
pb.collection("messages").subscribe(
"*",
(e) => { /* ... */ },
{
filter: "room = 'lobby'",
expand: "author",
}
);
Testing a subscription
With a subscription open in your app, trigger an event and watch it arrive.
Using the portal
Create, edit, or delete a record in the instance’s admin panel — every change made there is broadcast like any other.
Using the CLI
pbc admin use https://<instance-name>.pocketbasecloud.com
pbc admin login
pbc admin records create messages '{"text":"hello from the terminal","room":"lobby"}'
If nothing arrives, check the collection’s View rule before suspecting the connection — see Realtime respects your API rules.
Connection handling
The SDK maintains a single SSE connection per client and automatically reconnects and re-submits your subscriptions if the connection drops — useful on flaky mobile networks. You generally don’t need to handle reconnection yourself.
Realtime vs push notifications
Realtime subscriptions only reach clients that are connected right now — an open browser tab or a running app. They are not push notifications: when the app is closed or the phone is asleep, nothing is delivered and nothing queues up for later.
For notifications that reach a closed app, keep PocketBase as the trigger
and send through a push provider (Firebase Cloud Messaging, APNs, Web Push,
or a wrapper like Expo or OneSignal). A hook
fires on the record change and calls the provider. For example, with
Expo’s push API and a pushToken field on each user:
// pb_hooks/push.pb.js
onRecordAfterCreateSuccess((e) => {
e.next();
try {
const recipient = e.app.findRecordById("users", e.record.get("recipient"));
const token = recipient.get("pushToken");
if (!token) return;
$http.send({
url: "https://exp.host/--/api/v2/push/send",
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ to: token, title: "New message", body: e.record.get("text") }),
timeout: 10,
});
} catch (err) {
e.app.logger().error("push failed", "error", String(err));
}
}, "messages");
Use realtime for the live UI while the app is open, and push for everything else — most chat apps need both.