Building a Real-Time Analytics Dashboard with React and D3.js
Creating an interactive dashboard to visualize real-time data streams — WebSocket connections, dynamic chart updates, and TimescaleDB for time-series storage.
I built a real-time analytics dashboard that displays live metrics from a data pipeline, updating every second with new data points sourced from a WebSocket stream.
Technology Stack
| Layer | Technology |
|---|---|
| Frontend | React + TypeScript |
| Visualisation | D3.js & Chart.js |
| Backend | Node.js with Socket.IO |
| Database | PostgreSQL + TimescaleDB |
WebSocket Data Flow
Data flows from the ingestion layer → Kafka topic → Node.js consumer → Socket.IO broadcast → React state → Chart update — all within 50 ms end-to-end latency.
React Component
import React, { useEffect, useState } from 'react';
import { Line } from 'react-chartjs-2';
import io from 'socket.io-client';
interface DataPoint { ts: number; value: number; }
const Dashboard: React.FC = () => {
const [data, setData] = useState<DataPoint[]>([]);
useEffect(() => {
const socket = io('ws://analytics-server:3001');
socket.on('new-data', (point: DataPoint) => {
setData(prev => [...prev.slice(-100), point]);
});
return () => { socket.disconnect(); };
}, []);
const chartData = {
labels: data.map(d => new Date(d.ts).toLocaleTimeString()),
datasets: [{ label: 'Live Metric', data: data.map(d => d.value) }],
};
return <Line data={chartData} />;
};
Performance Metrics
| Metric | Value |
|---|---|
| Update frequency | 1 second |
| Data points rendered | 100 (rolling window) |
| End-to-end latency | < 50 ms |
| Concurrent users | 500+ |
TimescaleDB for Storage
TimescaleDB hypertables compress time-series data 20× compared to vanilla PostgreSQL and allow sub-second time_bucket() aggregations over months of data — critical for the “zoom out” feature in the dashboard.
-- Create hypertable
SELECT create_hypertable('metrics', 'time');
-- 1-minute rolling average
SELECT time_bucket('1 minute', time) AS bucket,
AVG(value) AS avg_value
FROM metrics
WHERE time > NOW() - INTERVAL '1 hour'
GROUP BY bucket
ORDER BY bucket;