🚨 This repository has been moved and will be archived on October 10, 2025
All development has moved to the Supabase JS Monorepo
If you're looking for the README of
realtime-js, you can find it at:
https://github.com/supabase/supabase-js/tree/master/packages/core/realtime-jsThis repository was merged into the main Supabase JS monorepo for better coordination, testing, and releases.
- 📖 For documentation: Visit the new realtime-js location
- 🐛 For issues: Create them in the supabase-js repository
- 🔧 For contributions: See the Contributing Guide
- 📚 For migration help: Read the Migration Guide
- Uncommitted changes: Manually transport your work to the monorepo (file structure is the same under
packages/core/realtime-js/)- Open PRs: Tag a maintainer in your PR and we'll help you migrate it
- Issues: Will be transported to the supabase-js repository
⚠️ This is the old repository. Please use the supabase-js monorepo going forward.
Send ephemeral messages with Broadcast, track and synchronize state with Presence, and listen to database changes with Postgres Change Data Capture (CDC).
Guides · Reference Docs · Multiplayer Demo
This client enables you to use the following Supabase Realtime's features:
- Broadcast: send ephemeral messages from client to clients with minimal latency. Use cases include sharing cursor positions between users.
- Presence: track and synchronize shared state across clients with the help of CRDTs. Use cases include tracking which users are currently viewing a specific webpage.
- Postgres Change Data Capture (CDC): listen for changes in your PostgreSQL database and send them to clients.
npm install @supabase/realtime-jsimport { RealtimeClient } from '@supabase/realtime-js' const client = new RealtimeClient(REALTIME_URL, { params: { apikey: API_KEY }, }) const channel = client.channel('test-channel', {}) channel.subscribe((status, err) => { if (status === 'SUBSCRIBED') { console.log('Connected!') } if (status === 'CHANNEL_ERROR') { console.log(`There was an error subscribing to channel: ${err.message}`) } if (status === 'TIMED_OUT') { console.log('Realtime server did not respond in time.') } if (status === 'CLOSED') { console.log('Realtime channel was unexpectedly closed.') } })REALTIME_URLis'ws://localhost:4000/socket'when developing locally and'wss://<project_ref>.supabase.co/realtime/v1'when connecting to your Supabase project.API_KEYis a JWT whose claims must containexpandrole(existing database role).- Channel name can be any
string.
Your client can send and receive messages based on the event.
// Setup... const channel = client.channel('broadcast-test', { config: { broadcast: { ack: false, self: false } } }) channel.on('broadcast', { event: 'some-event' }, (payload) => console.log(payload) ) channel.subscribe(async (status) => { if (status === 'SUBSCRIBED') { // Send message to other clients listening to 'broadcast-test' channel await channel.send({ type: 'broadcast', event: 'some-event', payload: { hello: 'world' }, }) } })- Setting
acktotruemeans that thechannel.sendpromise will resolve once server replies with acknowledgement that it received the broadcast message request. - Setting
selftotruemeans that the client will receive the broadcast message it sent out. - Setting
privatetotruemeans that the client will use RLS to determine if the user can connect or not to a given channel.
Your client can track and sync state that's stored in the channel.
// Setup... const channel = client.channel( 'presence-test', { config: { presence: { key: '' } } } ) channel.on('presence', { event: 'sync' }, () => { console.log('Online users: ', channel.presenceState()) }) channel.on('presence', { event: 'join' }, ({ newPresences }) => { console.log('New users have joined: ', newPresences) }) channel.on('presence', { event: 'leave' }, ({ leftPresences }) => { console.log('Users have left: ', leftPresences) }) channel.subscribe(async (status) => { if (status === 'SUBSCRIBED') { const status = await channel.track({ 'user_id': 1 }) console.log(status) } })Receive database changes on the client.
// Setup... const channel = client.channel('db-changes') channel.on('postgres_changes', { event: '*', schema: 'public' }, (payload) => { console.log('All changes in public schema: ', payload) }) channel.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, (payload) => { console.log('All inserts in messages table: ', payload) }) channel.on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'users', filter: 'username=eq.Realtime' }, (payload) => { console.log('All updates on users table when username is Realtime: ', payload) }) channel.subscribe(async (status) => { if (status === 'SUBSCRIBED') { console.log('Ready to receive database changes!') } })You can see all the channels that your client has instantiatied.
// Setup... client.getChannels()It is highly recommended that you clean up your channels after you're done with them.
- Remove a single channel
// Setup... const channel = client.channel('some-channel-to-remove') channel.subscribe() client.removeChannel(channel)- Remove all channels
// Setup... const channel1 = client.channel('a-channel-to-remove') const channel2 = client.channel('another-channel-to-remove') channel1.subscribe() channel2.subscribe() client.removeAllChannels()This repo draws heavily from phoenix-js.
MIT.