Skip to content

Supabase Realtime: When SMB Sites Actually Need It (and When They Don’t)

Realtime sounds magical. For most SMB sites it’s a feature looking for a problem.

John Cravey with AIFounder4 min readUpdated Jul 6, 2026

Supabase Realtime is the broadcast layer that lets your app subscribe to database changes and receive updates over a WebSocket. It’s a real piece of infrastructure — well-built, scalable, and free up to a generous limit. It’s also, in our experience auditing client sites, the most over-used Supabase feature. Most SMB sites that use Realtime are paying for complexity they don’t need.

Free estimate · 2 minutes

Read the playbook. Now see it for your business.

The posts are the mechanism. The estimate below sketches the version we'd actually ship for your business, at your scale. About a minute, no opt-in.

What Realtime actually does

Three things. First, Postgres Changes: subscribe to INSERT/UPDATE/DELETE events on a table and receive them in your client. Second, Broadcast: send arbitrary messages to all subscribers of a channel (useful for chat, cursor presence, ephemeral state). Third, Presence: track which clients are connected to a channel right now (useful for online indicators).

All three run over WebSockets. The Supabase client SDK handles reconnection, replay, and authentication.

When Realtime is the right tool

  • Collaborative editing: multiple users editing the same document, seeing each other’s changes in real time.
  • Live dashboards: ops dashboards where metrics need to update without a page refresh.
  • Chat features: instant messaging between users.
  • Multi-device sync: a user has the app open on phone + laptop, changes on one need to reflect on the other.
  • Notifications: surface a new event to a logged-in user immediately.

When it’s overkill

Most marketing sites. Most admin dashboards. Most lead inboxes. A 30-second polling interval is usually fine for the use cases that look real-time but aren’t — and polling is dramatically simpler to debug, monitor, and scale.

We had a client request a real-time lead notification feature: when a new lead came in, the team should see it immediately on the admin page. We implemented it with Realtime. Three months later we ripped it out and replaced it with a 15-second polling fetch. The Realtime version had two production issues (a dropped WebSocket showed no leads at all for 6 hours; an RLS policy bug let one tenant see another’s leads briefly). The polling version has had zero issues in 18 months.

The Postgres Changes pattern

"use client";
import { useEffect, useState } from "react";
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

export function LiveLeadFeed() {
  const [leads, setLeads] = useState<unknown[]>([]);

  useEffect(() => {
    const channel = supabase
      .channel("leads")
      .on(
        "postgres_changes",
        { event: "INSERT", schema: "public", table: "submissions" },
        (payload) => setLeads((prev) => [payload.new, ...prev])
      )
      .subscribe();

    return () => { supabase.removeChannel(channel); };
  }, []);

  return <ul>{leads.map((l: any) => <li key={l.id}>{l.name}</li>)}</ul>;
}

RLS still applies — and that’s a trap

Realtime respects RLS policies. If your policy says “only authenticated users from site X can read row Y,” Realtime enforces that. But the policy is evaluated against the user’s JWT at subscribe time. If the JWT expires mid-session, the subscription quietly stops delivering events. If the user logs out, the subscription becomes a no-op without an obvious error. Both have bitten us.

Broadcast: when you don’t need persistence

Broadcast is for ephemeral state — cursor position, “user is typing,” a temporary notification. Messages aren’t persisted. If a client is offline when the message is sent, it never sees the message.

For chat where messages need to survive disconnect, use a regular `messages` table + Postgres Changes subscription. The DB is the source of truth; Realtime is the delivery mechanism.

Presence: where it earns its keep

Presence tracks who’s connected to a channel right now. We use it on one client’s collaborative dashboard so users see “3 team members viewing this report.” It’s the only piece of Realtime we use in production today.

Cost

Supabase Pro includes 500 concurrent realtime connections and 2M messages per month. Plenty for an SMB. If you’re building chat at scale or a multi-thousand-user collaborative app, you’ll exceed it — at which point Realtime is still 1/5 the cost of building the same on Pusher or Ably.

The polling-first principle

Before reaching for Realtime, ask: how stale can the data be before users notice? If the answer is anything more than 5 seconds, poll. A 15-second polling interval costs almost nothing in CPU or bandwidth, never has WebSocket drop bugs, and is trivially debuggable. The “feels real-time” bar is usually 5–15 seconds — only true real-time collaboration needs anything tighter.

When you do use Realtime, instrument it

Subscribe to your own channel’s health events. Log every reconnect, every error, every subscription state change. Without instrumentation, a broken Realtime is invisible — the page just stops updating. We pipe Realtime events into GA4 as custom events so we can see in aggregate whether subscriptions are stable across users.

How this lands across FH client work

Across the FH client book we use Realtime in exactly one place: a Presence indicator on one client’s shared dashboard. Everything else — lead feeds, project status pages, admin dashboards — uses polling on 5-to-15-second intervals. Code is simpler, monitoring is simpler, ops incidents are zero. If you’ve been talked into Realtime for a feature that really only needs polling, book a consultation — the rip-out is usually a clean win.

Answers

Frequently asked questions

What does Realtime actually do?

It pushes database changes, broadcast messages, or presence state to connected clients over a websocket. The value is removing the delay between something changing and a user seeing it. The cost is a persistent connection and a class of bugs that only appear with multiple clients.

When is Realtime the right tool?

When multiple people are looking at the same changing data and staleness has a real cost: a shared board, a live queue, a chat surface, a dashboard being watched during an incident. The common thread is concurrency, not freshness on its own.

When is it overkill?

When one person is looking at data that changes rarely, which describes most small-business surfaces. A refresh on navigation or a poll every thirty seconds gives the same experience with none of the connection handling, and it fails in ways that are much easier to reason about.

What is the polling-first principle?

Start with a poll, measure whether the delay actually matters, and only then upgrade. Polling is trivially debuggable and degrades gracefully. Teams that start with websockets usually cannot answer what the latency requirement was, which means they cannot tell whether it is working.

Do RLS policies apply to Realtime?

Yes, and that is the trap: a subscription only delivers rows the subscriber can read, so a policy that looks fine for queries can silently deliver nothing over a subscription. The symptom is a feature that works locally with a service role and appears broken in production.

What is the difference between Postgres changes, broadcast, and presence?

Postgres changes stream row-level events from the database and are persistent by nature. Broadcast sends ephemeral messages between clients with no storage. Presence tracks who is currently connected. Choosing the wrong one usually means storing something that did not need storing.

When does broadcast beat listening to database changes?

When the message is transient and does not need to be recorded: a typing indicator, a cursor position, a live toast. Writing those to the database to get them delivered is a common mistake that turns an ephemeral signal into rows nobody will ever read.

What does Realtime cost?

It is priced on concurrent connections and message volume, so the cost profile is different from request-based billing: an idle user with a tab open still counts. A dashboard left open on a wall for a month is a cost nobody predicted when they added it.

How should Realtime be instrumented?

Track connection counts, reconnects, and message rates. Reconnect storms are the classic production failure, and without instrumentation the report you get is that the app feels slow. Silent reconnection loops can also multiply cost without changing anything visible.

What happens when a connection drops?

The client reconnects and, unless you handle it, has a gap where it missed events. Any subscription-driven UI needs a resync on reconnect, because the alternative is a view that quietly diverges from the database and stays wrong until a reload.

Can Realtime replace a refresh mechanism entirely?

Rarely, and it should not try. The robust pattern is a subscription that triggers a refetch rather than one that reconstructs state from events. Rebuilding state from a stream means every missed message is a permanent divergence.

How do I decide in one question?

Ask what breaks if the user sees this five seconds late. If the honest answer is nothing, poll. That single question resolves most Realtime decisions correctly and saves a class of infrastructure that would otherwise need maintaining forever.

Question we did not answer? Ask us directly and we will answer it here.

John Cravey, Founder
Written by
John Cravey
Founder

Founder of Frontend Horizon. Writes most of the long-form work on the FH blog.

Newer post
AI Chat for Customer Service on SMB Sites: When It Helps and When It Hurts
Older post
GA4 Attribution Models: Which One to Look At When
Keep reading

More from the blog

Supabase·6 min

Supabase Row Level Security: The Multi-Tenant Pattern We Use Across FH Clients

One Postgres database, many tenants, zero data leakage. Here’s the RLS setup that holds up under real production traffic.

Supabase·5 min

Supabase Storage for Marketing Sites: The Bucket-Per-Tenant Pattern

Most teams store images in their build artifact. That doesn’t scale. Supabase Storage with the right bucket layout does.

Next.js·5 min

Server Actions for Lead Forms: Replacing Your API Routes Without Losing Sleep

Server actions cut form code in half and ship progressively enhanced HTML. Here’s how to use them without leaking a database query.