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.
9 minute lesson
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 observes changes from database replication and streams them to subscribers.
Broadcast is the workhorse:
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' },
})
The event goes from one client through Realtime to the other subscribers. The database is not involved, which is exactly why it stays cheap under chatty traffic. Current Supabase guidance prefers Broadcast for scalable fan-out in many 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 can become noisy — every join, leave, and state update fans out to every subscriber, so keep the tracked state tiny.
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 sake and a few clients want to observe it. Database subscriptions still need filters and authorization, and the server checks each change against each subscriber — cost grows with writes and with listeners.
Classify before you build
Run the exercise’s three cases through the decision. Chat messages: high volume, clients talking to clients — Broadcast, persisting messages separately on your own terms. Online cursors: ephemeral shared state — Presence, throttled hard. A low-volume admin table update 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 not send every UI action through Postgres Changes: you pay for each event twice — once as a table write, once as replication fan-out — and busy channels start lagging behind the conversation they carry. Choose the smallest Realtime feature for each job.
Lesson completed