Setting Up Supabase Realtime

Supabase Realtime broadcasts database changes (INSERT, UPDATE, DELETE) to subscribed clients over a WebSocket connection. To enable it:

1. In your Supabase dashboard, go to Database → Replication 2. Enable replication for the tables you want to watch (e.g., `metrics`, `events`, `orders`) 3. Set RLS policies on those tables, Supabase Realtime respects RLS, so users only receive changes to rows they're allowed to see

This takes 2 minutes. The hard work is on the frontend, handling the stream of changes and updating the UI.

Connecting WeWeb to Supabase Realtime

WeWeb's Supabase plugin supports realtime subscriptions natively. In WeWeb:

1. Go to Plugins → Supabase → your data source 2. Enable "Realtime" on the collection you want to watch 3. WeWeb automatically subscribes to changes and updates bound elements when a change arrives

For a metrics dashboard bound to a `metrics` table: add a Collection bound to the table, enable Realtime, and bind chart/number elements to the collection. When a new metric row is inserted in Supabase, the dashboard updates in under 500ms without any user action. Need help building this? Our WeWeb developers specialise in WeWeb + Supabase real-time dashboards.

Building the Dashboard Layout

A typical real-time dashboard has three zones:

**KPI row**: 3–4 large number cards at the top. Each bound to an aggregate (SUM, COUNT, AVG) from a Supabase view. In WeWeb, create a view in Supabase that pre-calculates aggregates and bind the KPI cards to that view's realtime collection.

**Charts**: Line chart for time-series data, bar chart for comparisons, pie for distribution. WeWeb has a built-in Chart component powered by Chart.js. Bind the `data` prop to a Supabase collection ordered by `created_at`.

**Data table**: A sortable, filterable table for the raw events. WeWeb's DataGrid component with search, sort, and pagination. Enable Realtime so new rows appear at the top as they arrive.

Time Range Filtering

Every dashboard needs time range controls. Pattern:

1. Add a Filter Bar component with preset buttons: Last 24h, Last 7d, Last 30d, Custom range
2. Create a page variable `dateRange` with start and end timestamps
3. Pass `dateRange` as a filter to every Supabase collection: `.gte("created_at", dateRange.start).lte("created_at", dateRange.end)`
4. When the user selects a different range, update `dateRange`, all collections re-fetch automatically

For custom date ranges, use WeWeb's Date Picker component and bind both inputs to `dateRange.start` and `dateRange.end`.

Row-Level Security for Multi-Tenant Dashboards

For SaaS dashboards where each client sees only their data:

```sql
-- metrics table RLS policy
CREATE POLICY "Users see own org metrics"
ON metrics FOR SELECT
USING (
  org_id IN (
    SELECT org_id FROM memberships
    WHERE user_id = auth.uid()
  )
);
```

This policy means the same dashboard URL works for all clients, each sees only their organisation's data. No filtering needed in WeWeb. Supabase enforces it at the database level.

Critical: test this by logging in as users from two different organisations and confirming the data is isolated. An RLS bug in a dashboard is a serious data leak.

Performance Optimisation for Large Datasets

Dashboards with millions of rows slow down quickly if you're running aggregates on raw data.

**Create Supabase views for aggregates**: `CREATE MATERIALIZED VIEW daily_metrics AS SELECT DATE(created_at), COUNT(*), SUM(value) FROM events GROUP BY DATE(created_at)`. Refresh the materialized view hourly via a Supabase Edge Function cron job.

**Use indexes**: Add an index on `(org_id, created_at)` for every table filtered by organisation and time. This turns a 5-second query into a 50ms query.

**Paginate the raw table**: Never load all rows into the dashboard. Use Supabase's `.range(0, 99)` for the data table and let users paginate. This keeps initial load fast regardless of total row count.

Supabase Realtime Subscriptions Deep Dive

Supabase Realtime uses PostgreSQL's logical replication feature to capture every row-level change and broadcast it to subscribed clients via a WebSocket. Under the hood, Supabase runs a Realtime server (open source, available on GitHub) that listens to the PostgreSQL replication log and forwards change events to connected clients in real time.

There are two types of Realtime channels in Supabase. Database Changes listens to INSERT, UPDATE, and DELETE operations on specific tables, with optional filtering by column value (for example, only changes where `org_id = 'abc123'`). Broadcast allows clients to send arbitrary messages to other clients subscribed to the same channel, useful for collaborative features like "user X is currently editing this record" indicators.

For a dashboard, you primarily use Database Changes. Subscribe to the specific table and event type that matters: `channel.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'orders' }, (payload) => { /* update UI */ })`. The `payload` object contains the new row data, so you can append it to the existing collection without re-fetching the entire dataset. This makes realtime efficient, each update is a surgical add to the local state, not a full re-query.

Optimising Realtime for Multiple Concurrent Users

When a dashboard has hundreds or thousands of concurrent users all subscribed to the same table, the Supabase Realtime server broadcasts the change to every subscriber. This is efficient by design, one database change triggers one broadcast that fans out to all clients. However, client-side handling must be efficient to avoid UI jank under high update frequency.

For dashboards that receive dozens of updates per second (trading platforms, logistics monitoring, IoT sensor data), batching UI updates is essential. Instead of re-rendering on every event, collect events in a buffer and apply them in batches every 250ms. This keeps the UI smooth while still reflecting data that is at most 250ms old, more than real-time enough for human perception.

Filter subscriptions as narrowly as possible. Rather than subscribing to all changes on the `events` table, subscribe only to changes where `org_id = currentUser.orgId`. This reduces the number of events each client receives and reduces the load on the Realtime server. In WeWeb, the Supabase plugin supports filter parameters on realtime subscriptions, always set the org or user filter to scope the subscription.

Dashboard Data Refresh Strategies

Not every piece of dashboard data benefits from realtime subscriptions. Some data changes infrequently (account settings, user profile), some changes at a known schedule (daily totals, weekly reports), and some changes continuously (live events, active sessions). Matching the refresh strategy to the data type produces a better-performing dashboard.

For continuously changing data (order counts, active users, live revenue), Supabase Realtime subscriptions are the right choice. Data updates in under 500ms from the database insert to the dashboard update. For data that changes on a schedule (yesterday's totals, last week's summary), a manual refresh or a timed re-fetch every 60 seconds is sufficient, no realtime subscription needed.

In WeWeb, implement the timed re-fetch using a `setInterval` in a JavaScript action on page load, calling `ww.collections.refresh('your-collection-name')` on the configured interval. For a hybrid dashboard with both live and scheduled data, realtime handles the live feeds while timed refreshes keep the summary cards accurate. This combination is more efficient than subscribing everything to realtime, it reduces WebSocket event volume and avoids unnecessary re-renders of stable data.

Handling Realtime Errors Gracefully

WebSocket connections drop. Mobile users switch networks. Corporate firewalls block WebSocket upgrades. A production realtime dashboard must handle connection drops gracefully rather than silently showing stale data.

Supabase's Realtime JavaScript client has built-in reconnection logic. When the WebSocket disconnects, the client automatically attempts to reconnect with exponential backoff. However, during the disconnected period, changes made to the database are not received by the client. When reconnection succeeds, you should re-fetch the affected collections to catch up on missed changes, a gap fetch.

In WeWeb, implement the gap fetch by subscribing to the Supabase channel's `onError` and `onClose` events using a custom JavaScript action. When either event fires, set a page variable `connectionStatus` to `'reconnecting'` and show a visible status indicator on the dashboard. When the channel reconnects (`onJoin` event), refresh all realtime collections and reset `connectionStatus` to `'connected'`. This pattern ensures users always know whether the data they're seeing is live and up-to-date.

Financial Dashboard Case Study

One of the most demanding realtime dashboard implementations we've built at App Studio is a financial operations dashboard for a fintech startup processing payments across several European markets. The dashboard tracks live transaction volume, approval rates by payment method, fraud flags, and settlement status, updated in real time as transactions are processed.

The architecture uses Supabase as the central data warehouse. An Edge Function receives webhook payloads from the payment processor and inserts processed transaction records into the `transactions` table. The Realtime subscription in WeWeb fires on each insert, and the dashboard updates the live counters, adds the new transaction to the data table, and recalculates the approval rate chart, all within 600ms of the payment being processed.

Key implementation details that made this work at scale: materialised views for hourly aggregates (avoiding expensive COUNT/SUM on the full transactions table), a composite index on `(org_id, created_at, status)` for the common query pattern, and UI update batching to handle bursts of 20–30 transactions per second during peak hours. The dashboard handles 50,000+ daily transactions with no performance degradation, running on Supabase's Pro plan. This is the same architecture we apply to every data-intensive WeWeb dashboard we build.