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 EleviFounder4 min read

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.

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.

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.