← Back to Blog
Feb 20, 2026 · 8 min read

Building a Real-Time Dashboard with React and WebSockets

When we built the Meridian Analytics dashboard, the core requirement was simple: campaign data should update in real-time without the user refreshing the page. Here's how we architected it.

Architecture Overview

The dashboard has three layers: a React frontend with Chart.js for visualization, a Supabase backend for data storage and auth, and Supabase Realtime (built on WebSockets) for live updates.

Client (React + Chart.js)
    ↕ WebSocket connection
Supabase Realtime
    ↕ Postgres changes
Supabase Database (PostgreSQL)
    ↑ ETL pipeline (every 5 min)
Marketing Platform APIs

Why Supabase Realtime Over Raw WebSockets

We considered building a custom WebSocket server with Socket.io, but Supabase Realtime gave us two key advantages: automatic reconnection handling and row-level security. When a new campaign metric is inserted into PostgreSQL, Supabase broadcasts the change to all subscribed clients — no custom pub/sub infrastructure needed.

Performance Optimizations

With 12 chart types and thousands of data points, rendering performance was critical. Our approach:

  • Virtualized rendering: Only charts in the viewport are rendered. Off-screen charts use placeholder divs.
  • Incremental updates: Instead of re-fetching all data on each WebSocket event, we append new points to existing chart datasets.
  • Debounced re-renders: Rapid-fire updates are batched into 500ms windows to prevent layout thrashing.
  • Web Workers: Heavy data transformations (aggregations, rolling averages) run off the main thread.

Results

The final dashboard loads in under 400ms, handles 12 simultaneous chart updates, and maintains 60fps scrolling even on mid-range devices. The client's team went from spending 2 hours compiling daily reports to checking a single real-time dashboard.

Key Takeaways

  • Use managed WebSocket services (Supabase, Ably, Pusher) unless you need custom protocol handling
  • Batch rapid updates — your charts don't need 60 updates per second
  • Profile rendering performance early, not after the client demos
  • Incremental data updates beat full re-fetches every time