Adapters

Supabase

Point Flux at an existing Supabase client and it takes care of turning Postgres change events into live store updates — reconnect handling, single-socket coordination across tabs, and catch-up after time offline all come for free once the adapter is wired in.

Prerequisites
  • An existing Supabase project with a table you want to sync
  • The Supabase JS client already installed (@supabase/supabase-js)
  • @tsworldtech/flux and @tsworldtech/flux-supabase installed
1

Create the adapter

Pass your existing Supabase client straight into createSupabaseAdapter(), then hand that to createFlux(). There's no separate connect step and nothing to await — the adapter opens a connection lazily, only once a store actually registers a channel.
typescript
1import { createClient } from '@supabase/supabase-js'
2import { createFlux } from '@tsworldtech/flux'
3import { createSupabaseAdapter } from '@tsworldtech/flux-supabase'
4
5const supabase = createClient(
6 process.env.NEXT_PUBLIC_SUPABASE_URL!,
7 process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
8)
9
10const flux = createFlux({
11 adapter: createSupabaseAdapter(supabase),
12})
2

Register a store against a table

Registration is where a store gets connected to a real table. table is the actual Postgres table name; channel is just the name your app uses internally and can be anything you like. event controls which row operations you care about — use '*' for all of them, or scope down to just INSERT, UPDATE, or DELETE if that's all a given store needs.
typescript
1flux.register({
2 store: useTeamStore,
3 channel: 'team_members',
4 table: 'team_members', // actual Postgres table name
5 event: '*', // INSERT, UPDATE, DELETE, or '*' for all
6 idbKey: 'team:members',
7 ttl: 'medium',
8 ingestionType: 'COLLECTION_ALL',
9 diffBeforeUpdate: true,
10 hydrateState: (store, data) => {
11 // store.getState().setState handles both array hydrations and delta frames
12 store.getState().setState(data)
13 },
14})
3

Turn on replication for the table

Supabase requires an explicit opt-in per table before it will emit realtime change events at all.
Enable replication in the dashboard

Realtime replication is off by default per table in Supabase. In the Supabase dashboard, go to Database → Replication, and toggle on the tables you want Flux to receive live updates for. A table that isn't toggled on will still work for your initial data load — it just won't push live updates until replication is enabled for it.

4

Delta Catch-up & Database Setup

To enable retroactive catch-up after a client goes offline, run the flux_changelog SQL script in your Supabase SQL Editor.
Automatic Sync Anchor RPC Integration

While live push updates work out-of-the-box once table Replication is enabled, catch-up delta backfill (receiving what changed while offline) requires running the single-state flux_changelog SQL script in your Supabase SQL Editor. The adapter invokes the flux_request_delta RPC function on reconnect automatically.

5

Channel name vs. table name

These two fields are easy to mix up when a table's real name doesn't match how you refer to it in your app. Keep in mind that channel never has to match table — Flux only ever uses table to decide what to subscribe to on the Postgres side.
typescript
1// channel is just your internal name — table is the real Postgres table
2flux.register({
3 channel: 'pricing_matrix', // whatever name you want to use in your app
4 table: 'pricing', // the actual table Supabase watches
5 // ...
6})
6

Row-level security just works

You don't need to think about security separately for the realtime path versus your normal queries.
RLS applies to realtime the same way it applies to reads

Supabase's realtime replication respects Row Level Security the same way normal reads do. If a row wouldn't be visible to a signed-in user in a regular select, they won't receive an update for it over the realtime channel either. There's nothing extra to configure on the Flux side for this — it inherits whatever policies you've already set on the table.

Everything else — reconnecting after a dropped connection, making sure only one browser tab holds the live socket at a time, and catching back up on whatever changed while you were offline — happens automatically once these steps are done. There's nothing further to configure for a standard table.

For what happens after a reconnect specifically, see core-concepts/sync-anchors and core-concepts/activity-bus. For tables with a lot of rows where you only want detail pages cached on demand rather than all at once, see ingestion/lru.