Building Real-time Features with Supabase
Real-time functionality has become essential for modern web applications. From collaborative editing to live notifications, users expect instant updates. Supabase makes it remarkably simple to add real-time features to your applications.
Understanding Supabase Realtime
Supabase Realtime is built on PostgreSQL's replication functionality and uses WebSockets to push changes to connected clients. It supports three main features:
- Database changes (inserts, updates, deletes)
- Presence (tracking online users)
- Broadcast (sending messages between clients)
Setting Up Realtime
First, enable Realtime for your table:
ALTER PUBLICATION supabase_realtime ADD TABLE messages;
Then subscribe to changes in your application:
const channel = supabase
.channel('messages')
.on('postgres_changes',
{ event: '*', schema: 'public', table: 'messages' },
(payload) => {
console.log('Change received!', payload)
}
)
.subscribe()
Building a Chat Application
Let's build a real-time chat:
// Subscribe to new messages
const subscribeToMessages = (roomId: string) => {
return 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()
}// Send a message const sendMessage = async (content: string) => { const { error } = await supabase .from('messages') .insert({ content, room_id: roomId, user_id: userId }) if (error) console.error('Error sending message:', error) } ```
Implementing Presence
Track who's online in real-time:
const channel = supabase.channel('online-users')// Track presence channel .on('presence', { event: 'sync' }, () => { const state = channel.presenceState() const users = Object.values(state).flat() setOnlineUsers(users) }) .subscribe(async (status) => { if (status === 'SUBSCRIBED') { await channel.track({ user_id: userId, username: username, online_at: new Date().toISOString() }) } }) ```
Broadcast for Ephemeral Data
Send temporary data without touching the database:
// Typing indicators
const sendTypingStatus = () => {
channel.send({
type: 'broadcast',
event: 'typing',
payload: { user_id: userId, username: username }
})
}// Listen for typing channel.on('broadcast', { event: 'typing' }, (payload) => { setTypingUsers(prev => [...prev, payload.payload.username]) }) ```
Performance Optimization
Filtering at the Database Level
Always filter subscriptions to reduce bandwidth:
.on('postgres_changes',
{
event: '*',
schema: 'public',
table: 'posts',
filter: `organization_id=eq.${orgId}`
},
handleChange
)
Throttling Updates
Prevent overwhelming clients with too many updates:
import { throttle } from 'lodash'const throttledUpdate = throttle((payload) => { updateUI(payload) }, 100) ```
Error Handling
Always handle connection errors gracefully:
channel
.subscribe((status, error) => {
if (status === 'CHANNEL_ERROR') {
console.error('Subscription error:', error)
// Attempt reconnection
setTimeout(() => channel.subscribe(), 5000)
}
})
Best Practices
- Use filters to minimize unnecessary data transfer
- Implement reconnection logic for network issues
- Clean up subscriptions when components unmount
- Use broadcast for ephemeral data
- Test with poor network conditions
Conclusion
Supabase Realtime makes it straightforward to add real-time features to your applications. By understanding the three core features—database changes, presence, and broadcast—you can build engaging, collaborative experiences that users love.