How to Implement Real-Time Features with Supabase Realtime Subscriptions
๐ Want the best deal? Check current prices and availability.
Compare Prices โWhen you buy through links on our site, we may earn a commission.
How to Implement Real-Time Features with Supabase Realtime Subscriptions
The first time you ship a live feature, you learn something uncomfortable: users don't refresh. They open a tab, leave it there for six hours, and expect the numbers on screen to be correct. The moment one person sees a stale task list while their teammate updates it, you've lost a bit of trust.
Supabase Realtime is one of the more practical answers to that problem, mostly because it doesn't ask you to rewrite your data layer. If your app already talks to Postgres, you can bolt live updates on top of the tables you already have. No separate document store, no event log you have to keep in sync by hand.
This tutorial walks through the three primitives Supabase Realtime gives you โ Postgres Changes, Broadcast, and Presence โ with working code, the gotchas that bite people in production, and an honest look at when you should reach for something else.
What Supabase Realtime Actually Ships
There's a tendency to treat "Realtime" as one feature. It's really four, and picking the wrong one is the most common mistake I see.
1. Postgres Changes
This is the headline act. You subscribe to a table, and Supabase pushes you INSERT, UPDATE, and DELETE events as they happen. Under the hood it reads the Postgres write-ahead log through a logical replication slot, then fans those changes out over WebSockets.
The important detail: Row Level Security is enforced per subscriber. If your policy says a user can only see rows where org_id = 'abc', they only receive events for those rows. That's genuinely hard to replicate yourself and it's the main reason teams pick this over rolling their own CDC pipeline.
2. Broadcast
Ephemeral pub/sub over a channel. No database involved, no persistence. You publish a message, everyone subscribed to that topic gets it. This is what you want for cursor positions, "user is typing," live vote counters, or anything firing more than a few times per second.
3. Presence
Presence rides on top of a channel and tracks who's connected, plus whatever metadata you attach. You get sync, join, and leave events. Under the hood Supabase uses a CRDT so presence state converges correctly when clients drop and reconnect โ a detail that matters more than it sounds when someone's laptop sleeps mid-session.
4. Realtime Authorization (private channels)
Since early 2024, channels can be marked private. Access is then governed by RLS policies on the realtime.messages table, so you can gate a channel topic the same way you'd gate a table. There's also a newer "Broadcast from Database" feature that lets a Postgres trigger push a broadcast without your client being involved at all.
How the plumbing works
Supabase runs Realtime as a separate Elixir/Phoenix cluster sitting next to your Postgres instance. Each node owns a set of channels; changes stream from Postgres via logical replication, get filtered against each subscriber's RLS context, and go out over WebSocket. Your database stays the source of truth โ Realtime is a delivery mechanism, not a second copy of your data.
Building a Live Task Board
Let's build something concrete: a shared task board where cards appear, move, and disappear for everyone in the room, plus live "who's here" indicators and a typing-style activity feed.
Setup
npm install @supabase/supabase-js
// lib/supabase.js
import { createClient } from '@supabase/supabase-js'
export const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY
)
Use the anon key on the client. It's designed to be public โ RLS is what protects your data, not key secrecy.
Step
๐ Want the best deal? Check current prices and availability.
Compare Prices โ