Storage and Realtime
Choose a Realtime feature
Select Broadcast, Presence, or Postgres Changes according to whether the application sends events, tracks participants, or observes database rows.
Supabase Realtime is one websocket connection carrying three different tools, and they are not interchangeable. Broadcast sends application events between connected clients. Presence tracks shared client state, like who is online. Postgres Changes watches the database and streams row changes to subscribers.
Let’s see each one, then decide when to use which.
Broadcast is the workhorse. Clients join a channel, listen for an event name, and send:
const channel = supabase.channel('room:42')
channel.on('broadcast', { event: 'message' }, ({ payload }) => {
console.log(payload.text)
})
await channel.subscribe()
channel.send({
type: 'broadcast',
event: 'message',
payload: { text: 'hello from Ada' },
})
Every other subscriber of room:42 logs hello from Ada. The event goes from one client through Realtime to the others. The database is not involved, and that is exactly why it stays cheap under chatty traffic. Current Supabase guidance prefers Broadcast for scalable fan-out in most cases.
Presence answers “who is here right now”:
channel.on('presence', { event: 'sync' }, () => {
console.log(Object.keys(channel.presenceState()).length, 'online')
})
Presence is useful but noisy. Every join, leave, and state update fans out to every subscriber. Keep the tracked state tiny: a user ID and maybe a cursor position, not a whole profile.
Postgres Changes turns the database into the event source:
supabase.channel('settings-watch')
.on('postgres_changes',
{ event: 'UPDATE', schema: 'public', table: 'settings' },
payload => console.log('settings changed', payload.new))
.subscribe()
It shines when the write already happens for its own reasons and a few clients want to know about it. But database subscriptions still need filters and authorization, and the server checks each change against each subscriber. Cost grows with writes and with listeners, at the same time.
Classify before you build
Run three common cases through the decision. Chat messages: high volume, clients talking to clients. That is Broadcast, and you persist the messages separately on your own terms. Online cursors: ephemeral shared state. That is Presence, throttled hard. A low-volume admin table that a dashboard should reflect: Postgres Changes fits.
The mistake to avoid is routing every UI action through database changes because one mechanism feels tidier. Do that and you pay for each event twice, once as a table write and once as replication fan-out. Busy channels start lagging behind the conversation they carry, and users notice.
My advice is to choose the smallest Realtime feature that does the job, one channel at a time. Broadcast first. Presence only for state that really is shared. Postgres Changes only when the database is the source of truth and the volume is low.
Lesson completed