DEV Community

Alex Spinov
Alex Spinov

Posted on

Supabase Realtime Has Free WebSocket Subscriptions — Here's How to Build Live Features

Building real-time features usually means setting up WebSocket servers, managing connections, and handling reconnects. Supabase Realtime does all of this for you.

What is Supabase Realtime?

Supabase Realtime lets you listen to database changes, broadcast messages between clients, and sync presence (who is online) — all over WebSockets, all for free.

Three Realtime Features

1. Database Changes (Postgres Changes)

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);

// Listen to ALL changes on the messages table
const channel = supabase
  .channel('messages')
  .on('postgres_changes', {
    event: '*', // INSERT, UPDATE, DELETE
    schema: 'public',
    table: 'messages',
  }, (payload) => {
    console.log('Change:', payload.eventType, payload.new);
  })
  .subscribe();

// Listen to specific events
supabase
  .channel('new-orders')
  .on('postgres_changes', {
    event: 'INSERT',
    schema: 'public',
    table: 'orders',
    filter: 'status=eq.pending',
  }, (payload) => {
    showNotification(`New order: ${payload.new.id}`);
  })
  .subscribe();
Enter fullscreen mode Exit fullscreen mode

2. Broadcast (Client-to-Client Messages)

// Send cursor positions, typing indicators, etc.
const channel = supabase.channel('room-1');

// Send
channel.send({
  type: 'broadcast',
  event: 'cursor',
  payload: { x: 100, y: 200, userId: 'alice' },
});

// Receive
channel
  .on('broadcast', { event: 'cursor' }, (payload) => {
    updateCursorPosition(payload.payload);
  })
  .subscribe();
Enter fullscreen mode Exit fullscreen mode

3. Presence (Who Is Online)

const channel = supabase.channel('online-users');

// Track your presence
channel.subscribe(async (status) => {
  if (status === 'SUBSCRIBED') {
    await channel.track({
      userId: 'alice',
      status: 'online',
      lastSeen: new Date().toISOString(),
    });
  }
});

// Listen for presence changes
channel.on('presence', { event: 'sync' }, () => {
  const state = channel.presenceState();
  console.log('Online users:', Object.keys(state).length);
});

channel.on('presence', { event: 'join' }, ({ key, newPresences }) => {
  console.log('User joined:', newPresences);
});

channel.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
  console.log('User left:', leftPresences);
});
Enter fullscreen mode Exit fullscreen mode

React Example: Live Chat

import { useEffect, useState } from 'react';
import { supabase } from './supabaseClient';

function LiveChat({ roomId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    // Load initial messages
    supabase
      .from('messages')
      .select('*')
      .eq('room_id', roomId)
      .order('created_at')
      .then(({ data }) => setMessages(data || []));

    // Subscribe to new messages
    const channel = supabase
      .channel(`room-${roomId}`)
      .on('postgres_changes', {
        event: 'INSERT',
        schema: 'public',
        table: 'messages',
        filter: `room_id=eq.${roomId}`,
      }, (payload) => {
        setMessages(prev => [...prev, payload.new]);
      })
      .subscribe();

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

  const sendMessage = async (text) => {
    await supabase.from('messages').insert({
      room_id: roomId,
      text,
      user_id: (await supabase.auth.getUser()).data.user?.id,
    });
  };

  return (
    <div>
      {messages.map(msg => <p key={msg.id}>{msg.text}</p>)}
      <input onKeyDown={(e) => {
        if (e.key === 'Enter') {
          sendMessage(e.currentTarget.value);
          e.currentTarget.value = '';
        }
      }} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Free Tier Limits

  • 200 concurrent connections
  • 2 million messages/month
  • Unlimited channels

Need real-time data feeds from the web? Check out my Apify actors — monitor websites and get instant updates. For custom solutions, email spinov001@gmail.com.

Top comments (0)