Building Real-Time Dashboards: A Technical Guide
Technical guide to building real-time business dashboards in 2026. WebSocket architecture, data pipelines, visualization libraries, and deployment for Singapore businesses.
Quick Answer
Building real-time dashboards in 2026 means choosing between WebSocket connections (true real-time, sub-second updates, more complex) and polling (simpler, 5-30 second intervals, good enough for most business dashboards). The recommended stack: React or Next.js frontend, WebSocket via Socket.IO or native WS API, PostgreSQL with LISTEN/NOTIFY for database-driven events, Redis for caching and pub/sub, and Recharts or D3.js for visualization. Infrastructure cost for a Singapore-hosted real-time dashboard: S$150-S$500/month for small-to-medium businesses. Development cost: S$15,000-S$35,000 for a custom dashboard with 5-10 real-time data sources. For most Singapore SMEs, server-sent events (SSE) with 5-second polling is the sweet spot. It is simpler than WebSockets and fast enough for business metrics.
Your static dashboard refreshes once a day. Or maybe once an hour. By the time you see a problem, it's already a fire.
Real-time dashboards change that. Live data, instant visibility, faster decisions.
But "real-time" means different things depending on your architecture. True millisecond updates require different technology than 5-second refreshes. The wrong choice means over-engineering a simple problem or under-engineering a critical one.
Here's the technical guide based on dashboards we've built for Singapore businesses across logistics, e-commerce, finance, and SaaS.
Real-time vs polling: Choose your architecture
Polling (the simpler option)
How it works: Your frontend makes HTTP requests to your API at regular intervals. Every 5 seconds, 10 seconds, or 30 seconds, it asks "anything new?"
Implementation:
// Simple polling - React example
useEffect(() => {
const interval = setInterval(async () => {
const response = await fetch('/api/dashboard-data');
const data = await response.json();
setDashboardData(data);
}, 5000); // Poll every 5 seconds
return () => clearInterval(interval);
}, []);Pros:
- Simple to implement
- Works with any backend
- Easy to debug
- No special infrastructure needed
- Stateless: no connection management
Cons:
- Not truly real-time (minimum delay = polling interval)
- Wastes bandwidth when nothing has changed
- Server load increases linearly with connected clients
- At 100 clients polling every 5 seconds, that's 1,200 requests/minute
Best for: Business dashboards where 5-30 second delays are acceptable. Sales dashboards, weekly KPI views, most management dashboards.
Cost impact: Minimal. Standard hosting handles polling for up to 50-100 concurrent users easily.
Server-Sent Events (SSE)
How it works: The server maintains an open HTTP connection and pushes data to the client when something changes. One-directional: server to client only.
Implementation:
// Server-side (Node.js/Express)
app.get('/api/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const sendUpdate = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
// Listen for database changes and push
const listener = onDataChange((newData) => {
sendUpdate(newData);
});
req.on('close', () => {
removeListener(listener);
});
});
// Client-side
const eventSource = new EventSource('/api/events');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
setDashboardData(data);
};Pros:
- Simpler than WebSockets
- Built into browsers (no library needed)
- Automatic reconnection
- Works through most proxies and firewalls
- Lower overhead than polling (only sends when data changes)
Cons:
- One-directional only (server to client)
- Limited to ~6 concurrent connections per browser per domain (HTTP/1.1)
- No binary data support
- Some older corporate proxies may buffer responses
Best for: Dashboards that only need to display data (no user interaction back to server). This covers 80% of business dashboard use cases.
Our recommendation for most Singapore SMEs: SSE is the sweet spot. Simpler than WebSockets, more efficient than polling, and handles everything a typical business dashboard needs.
WebSockets (true bidirectional real-time)
How it works: A persistent, full-duplex connection between client and server. Either side can send data at any time. True real-time.
Implementation:
// Server-side (Node.js with ws library)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
// Send initial dashboard state
ws.send(JSON.stringify(getDashboardState()));
// Push updates when data changes
const listener = onDataChange((newData) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(newData));
}
});
ws.on('close', () => {
removeListener(listener);
});
});
// Client-side
const ws = new WebSocket('wss://your-api.com/dashboard');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
updateDashboard(data);
};Pros:
- True real-time (millisecond updates)
- Bidirectional communication
- Lower overhead per message than HTTP
- Binary data support
- Best for high-frequency updates
Cons:
- More complex to implement correctly
- Connection management (reconnection, heartbeats, scaling)
- Stateful: harder to load balance
- More server memory per connection
- Need WebSocket-compatible infrastructure
Best for: Dashboards with sub-second update requirements: live trading, real-time logistics tracking, live auction platforms, IoT sensor monitoring.
Which one should you pick?
Polling if: Updates every 30+ seconds are fine, you want the simplest possible implementation, you have fewer than 50 concurrent viewers.
SSE if: You need near-real-time updates (1-5 seconds), data flows server-to-client only, you want simplicity with good performance. This is the right choice for most business dashboards.
WebSockets if: You need sub-second updates, users interact with the dashboard in real-time (collaborative features, live controls), you have high-frequency data streams, or you need bidirectional communication.
Data pipeline architecture
Event-driven pipeline (recommended)
The best real-time dashboards don't query the database on every update. They react to events.
Architecture:
Data Source → Event Queue → Processing → Cache → Dashboard
(Database) (Redis/Kafka) (Aggregation) (Redis) (WebSocket/SSE)
How it works:
- Data source generates event: A new order comes in, a sensor reports a reading, a payment processes
- Event enters queue: Redis Pub/Sub for simple cases, Apache Kafka for high volume
- Processing service aggregates: Calculates running totals, averages, counts
- Cache updates: Pre-computed dashboard data stored in Redis
- Dashboard receives push: SSE or WebSocket pushes updated data to connected clients
Why this is better than direct database queries: Your dashboard isn't hammering the database. Pre-computed aggregations mean instant responses. The database handles writes, Redis handles reads.
PostgreSQL LISTEN/NOTIFY
PostgreSQL has a built-in pub/sub mechanism that's perfect for dashboard updates:
-- Create a trigger that notifies on changes
CREATE OR REPLACE FUNCTION notify_dashboard_change()
RETURNS trigger AS $$
BEGIN
PERFORM pg_notify(
'dashboard_updates',
json_build_object(
'table', TG_TABLE_NAME,
'action', TG_OP,
'data', row_to_json(NEW)
)::text
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Attach to your orders table
CREATE TRIGGER orders_dashboard_trigger
AFTER INSERT OR UPDATE ON orders
FOR EACH ROW
EXECUTE FUNCTION notify_dashboard_change();// Node.js listener
const { Client } = require('pg');
const client = new Client();
await client.connect();
await client.query('LISTEN dashboard_updates');
client.on('notification', (msg) => {
const payload = JSON.parse(msg.payload);
// Update dashboard cache and push to clients
updateDashboardCache(payload);
broadcastToClients(payload);
});Pros: No additional infrastructure. Works with your existing PostgreSQL database. Simple to set up.
Cons: Doesn't scale beyond a single database instance. Notifications are not persisted (if your listener is down, you miss events). Not suitable for very high-frequency updates (thousands per second).
Best for: Small-to-medium dashboards with up to a few hundred events per minute. Most Singapore SME dashboards fit this perfectly.
Redis for caching and pub/sub
Redis serves dual purpose in real-time dashboards: caching pre-computed data and distributing updates.
// Publisher (when data changes)
const Redis = require('ioredis');
const redis = new Redis();
// Store pre-computed dashboard data
await redis.hset('dashboard:sales', {
today_total: 15420,
today_orders: 47,
avg_order_value: 328,
last_updated: Date.now()
});
// Publish update event
await redis.publish('dashboard:updates', JSON.stringify({
metric: 'sales',
data: { today_total: 15420, today_orders: 47 }
}));// Subscriber (dashboard server)
const subscriber = new Redis();
subscriber.subscribe('dashboard:updates');
subscriber.on('message', (channel, message) => {
const update = JSON.parse(message);
// Push to all connected dashboard clients
broadcastSSE(update);
});Why Redis: Sub-millisecond reads. Perfect for dashboard data that changes frequently but doesn't need to be persisted in PostgreSQL. Reduces database load dramatically.
Cost: Redis hosting in Singapore (AWS ElastiCache or self-hosted): S$50-S$200/month depending on instance size.
Visualization libraries
Recharts (recommended for most projects)
What it is: A React charting library built on D3.js. Declarative, composable, easy to use.
Why we recommend it: Clean API, good performance with real-time data, excellent React integration, responsive by default.
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
function RealtimeSalesChart({ data }) {
return (
<ResponsiveContainer width="100%" height={300}>
<LineChart data={data}>
<XAxis dataKey="time" />
<YAxis />
<Tooltip />
<Line
type="monotone"
dataKey="revenue"
stroke="#2563eb"
strokeWidth={2}
dot={false}
isAnimationActive={false} // Disable animation for real-time
/>
</LineChart>
</ResponsiveContainer>
);
}Performance tip: Disable animations for real-time charts. Animations that trigger on every data update create jank and consume CPU.
Best for: React/Next.js projects, standard chart types (line, bar, area, pie), business dashboards.
Limitations: Complex custom visualizations (maps, network graphs, custom shapes) require D3.js directly.
D3.js (for complex visualizations)
What it is: The most powerful JavaScript visualization library. Low-level, imperative, fully customizable.
When to use: Custom map visualizations (Singapore district heatmaps), network/relationship graphs, unique chart types Recharts doesn't support, animation-heavy data stories.
When NOT to use: Standard business charts. D3.js is overkill for bar charts and line graphs. Use Recharts instead.
Real-time performance: D3.js can handle thousands of data points with proper implementation. Key techniques:
- Use
requestAnimationFramefor smooth updates - Implement data windowing (only render visible data points)
- Use Canvas rendering instead of SVG for datasets above 5,000 points
- Batch DOM updates
Chart.js (lightweight alternative)
What it is: Simple, lightweight charting library. Not React-specific.
When to use: Non-React projects, simple dashboards with basic charts, when bundle size matters (Chart.js is smaller than Recharts).
Limitations: Less customizable than Recharts or D3.js. React integration requires wrapper library (react-chartjs-2).
Comparison table
| Feature | Recharts | D3.js | Chart.js |
|---|---|---|---|
| Learning curve | Low | High | Low |
| Customization | Medium | Unlimited | Low |
| React integration | Native | Manual | Wrapper |
| Bundle size | ~200KB | ~250KB | ~60KB |
| Real-time support | Good | Excellent | Good |
| Best for | Business dashboards | Custom viz | Simple charts |
Our pick for Singapore business dashboards: Recharts. Good enough for 90% of use cases. Switch to D3.js only for specific visualization requirements.
Database choices for real-time
PostgreSQL (primary database)
Role: Source of truth. All business data lives here.
Real-time features: LISTEN/NOTIFY for event-driven updates, efficient time-series queries with proper indexing, JSONB for flexible dashboard configuration storage.
Performance tips for dashboard queries:
-- Create materialized view for dashboard aggregations
CREATE MATERIALIZED VIEW dashboard_daily_summary AS
SELECT
date_trunc('day', created_at) as day,
COUNT(*) as order_count,
SUM(total_amount) as revenue,
AVG(total_amount) as avg_order_value,
COUNT(DISTINCT customer_id) as unique_customers
FROM orders
WHERE created_at >= NOW() - INTERVAL '90 days'
GROUP BY date_trunc('day', created_at);
-- Refresh periodically (every 5 minutes for near-real-time)
REFRESH MATERIALIZED VIEW CONCURRENTLY dashboard_daily_summary;Indexing strategy: Create indexes on columns you filter by in dashboard queries (date ranges, status fields, category fields). A missing index on a date column can make a dashboard query 100x slower.
Redis (cache and real-time layer)
Role: Fast reads for dashboard data. Pre-computed aggregations. Pub/sub for real-time updates.
What to store in Redis:
- Current day's running totals
- Last 24 hours of time-series data points
- Active user counts
- Real-time alert thresholds
- Dashboard configuration/layout
What NOT to store in Redis:
- Historical data (belongs in PostgreSQL)
- Raw transaction data
- Anything you need to query with complex filters
TimescaleDB (for heavy time-series data)
What it is: PostgreSQL extension optimised for time-series data. Same SQL interface, dramatically better performance for time-based queries.
When to consider: IoT dashboards with thousands of sensor readings per minute, financial dashboards with tick-level data, monitoring dashboards with high-frequency metrics.
For most Singapore SME dashboards: Standard PostgreSQL is fine. TimescaleDB is for cases where you're ingesting thousands of data points per second.
Performance optimization
Frontend performance
1. Data windowing: Only render data points visible on screen.
// Instead of rendering all 10,000 data points
// Render only the last 100 (visible on chart)
const visibleData = allData.slice(-100);2. Throttle updates: If data arrives faster than the human eye can process, throttle rendering.
import { useCallback, useRef } from 'react';
function useThrottledUpdate(callback, delay = 200) {
const lastCall = useRef(0);
return useCallback((...args) => {
const now = Date.now();
if (now - lastCall.current >= delay) {
lastCall.current = now;
callback(...args);
}
}, [callback, delay]);
}3. Memoize chart components: Prevent unnecessary re-renders when unrelated data changes.
const SalesChart = React.memo(({ salesData }) => (
<ResponsiveContainer width="100%" height={300}>
<LineChart data={salesData}>
{/* ... */}
</LineChart>
</ResponsiveContainer>
));4. Use Web Workers for heavy computation: If your dashboard does client-side aggregation, move it off the main thread.
Backend performance
1. Pre-compute aggregations: Don't calculate totals on every request. Maintain running counters.
// Bad: Calculate on every request
app.get('/api/sales-today', async (req, res) => {
const result = await db.query(
'SELECT SUM(total) FROM orders WHERE date = CURRENT_DATE'
);
res.json(result);
});
// Good: Pre-computed, served from cache
app.get('/api/sales-today', async (req, res) => {
const cached = await redis.hgetall('dashboard:sales:today');
res.json(cached);
});
// Update cache when new order arrives (event-driven)
onNewOrder(async (order) => {
await redis.hincrby('dashboard:sales:today', 'total', order.amount);
await redis.hincrby('dashboard:sales:today', 'count', 1);
publishUpdate('sales');
});2. Connection pooling: Don't open a new database connection per request.
const { Pool } = require('pg');
const pool = new Pool({
max: 20, // Max 20 connections
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});3. Query optimization: Dashboard queries should return in under 100ms. If they don't, you need indexes or materialized views.
Network optimization
1. Send diffs, not full datasets: When data updates, send only what changed.
// Bad: Send entire dataset on every update
broadcastToClients({ allOrders: allOrdersArray }); // 50KB per update
// Good: Send only the change
broadcastToClients({ type: 'new_order', data: newOrder }); // 200 bytes2. Compress data: Enable gzip/brotli compression for SSE and WebSocket messages.
3. Use binary protocols for high-frequency data: If you're sending thousands of updates per second, consider Protocol Buffers or MessagePack instead of JSON.
Deployment and infrastructure
Recommended stack for Singapore
Hosting: AWS Singapore (ap-southeast-1) or Google Cloud Singapore (asia-southeast1)
Why Singapore region: Lower latency for local users (2-5ms vs 100-200ms for US/EU), PDPA compliance (data stays in Singapore), better performance for regional customers.
Infrastructure components:
| Component | Service | Monthly cost |
|---|---|---|
| Application server | AWS EC2 t3.medium or Fargate | S$80-S$150 |
| PostgreSQL | AWS RDS db.t3.medium | S$100-S$200 |
| Redis | AWS ElastiCache t3.micro | S$30-S$60 |
| Load balancer | AWS ALB | S$30-S$50 |
| CDN | CloudFront | S$10-S$30 |
| Monitoring | CloudWatch + basic alerting | S$10-S$20 |
| Total | S$260-S$510/month |
For smaller projects: A single S$40-S$80/month VPS (DigitalOcean, Vultr Singapore) can run the application server, PostgreSQL, and Redis together. Good enough for under 50 concurrent dashboard users.
WebSocket scaling
WebSockets are stateful. This makes horizontal scaling harder than stateless HTTP.
Problem: User connects to Server A via WebSocket. Server B receives a data update. How does User get the update?
Solution: Redis Pub/Sub as message broker between servers.
Server A ←WebSocket→ User 1, User 2
↕ (Redis Pub/Sub)
Server B ←WebSocket→ User 3, User 4
↕ (Redis Pub/Sub)
Server C ←WebSocket→ User 5, User 6
When data changes, the update is published to Redis. All servers subscribe and push to their connected clients.
Sticky sessions: Alternative to pub/sub. Load balancer routes the same user to the same server. Simpler but less resilient (if a server goes down, all its users disconnect).
For most Singapore SME dashboards: You don't need multiple servers. A single server handles 1,000+ concurrent WebSocket connections easily. Scale when you actually need to, not preemptively.
SSL/TLS for WebSockets
WebSocket connections must use WSS (WebSocket Secure) in production. Unsecured WS connections are blocked by most browsers on HTTPS pages.
// Wrong: ws:// on an HTTPS site
const ws = new WebSocket('ws://api.example.com/dashboard');
// Correct: wss:// always in production
const ws = new WebSocket('wss://api.example.com/dashboard');
Use your existing SSL certificate. If you're behind a load balancer (ALB, Nginx), configure WebSocket pass-through in the proxy settings.
Security for real-time data
Authentication
Problem: WebSocket connections bypass traditional HTTP authentication middleware.
Solution: Authenticate during the WebSocket handshake, not after.
// Server-side: Verify JWT during upgrade
const wss = new WebSocket.Server({ noServer: true });
server.on('upgrade', (request, socket, head) => {
// Extract token from query string or cookie
const token = extractToken(request);
if (!verifyToken(token)) {
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
socket.destroy();
return;
}
wss.handleUpgrade(request, socket, head, (ws) => {
ws.userId = decodeToken(token).userId;
wss.emit('connection', ws, request);
});
});Authorization (data filtering)
Not every user should see every metric. Filter data based on user permissions.
wss.on('connection', (ws) => {
const userPermissions = getUserPermissions(ws.userId);
const listener = onDataChange((update) => {
// Only send data user is authorized to see
const filteredUpdate = filterByPermissions(update, userPermissions);
if (filteredUpdate) {
ws.send(JSON.stringify(filteredUpdate));
}
});
});Rate limiting
Protect against connection flooding:
const connectionCounts = new Map();
wss.on('connection', (ws, req) => {
const ip = req.socket.remoteAddress;
const count = connectionCounts.get(ip) || 0;
if (count >= 10) { // Max 10 connections per IP
ws.close(1008, 'Too many connections');
return;
}
connectionCounts.set(ip, count + 1);
ws.on('close', () => {
connectionCounts.set(ip, connectionCounts.get(ip) - 1);
});
});PDPA considerations for Singapore
If your dashboard displays personal data (customer names, contact details, transaction history):
- Ensure data is encrypted in transit (WSS, not WS)
- Implement role-based access control
- Log who accesses what data and when
- Allow data to be anonymised/aggregated where possible
- Keep data on Singapore-hosted servers
Cost breakdown for Singapore businesses
Small dashboard (5-8 metrics, 10-20 users)
Development: S$15,000-S$20,000
- Frontend with 5-8 chart components
- SSE or polling backend
- PostgreSQL + Redis
- Authentication and role-based access
- Mobile-responsive design
Infrastructure: S$80-S$150/month
- Single VPS in Singapore
- Managed PostgreSQL
- Basic monitoring
Total first year: S$16,000-S$21,800
Medium dashboard (15-25 metrics, 50-100 users)
Development: S$25,000-S$35,000
- 15-25 chart/metric components
- WebSocket or SSE backend
- Multiple data sources
- Advanced filtering and drill-down
- Export capabilities (PDF, CSV)
- Custom alerts and notifications
Infrastructure: S$250-S$500/month
- Application server (EC2 or Fargate)
- RDS PostgreSQL
- ElastiCache Redis
- Load balancer
- Monitoring and alerting
Total first year: S$28,000-S$41,000
Enterprise dashboard (50+ metrics, 500+ users)
Development: S$50,000-S$100,000
- Comprehensive metric library
- Multiple dashboard views by role
- Real-time collaboration features
- Complex event processing
- Advanced security and audit logging
- API for third-party integrations
Infrastructure: S$800-S$2,000/month
- Multiple application servers
- Database cluster
- Redis cluster
- CDN
- Advanced monitoring
- Disaster recovery
Total first year: S$59,600-S$124,000
PSG Grant applicability
Custom dashboard development is often PSG or EDG eligible:
- PSG: If using pre-approved dashboard solution (limited options)
- EDG: Custom dashboard development qualifies (up to 50% coverage)
Example: S$30,000 custom dashboard → S$15,000 after EDG → S$15,000 out of pocket
Common pitfalls
Pitfall 1: Over-engineering real-time
You built a WebSocket system with Kafka message queue and Redis Streams for a dashboard that updates sales totals hourly. A simple cron job refreshing a cache would have taken 2 days to build instead of 4 weeks.
Rule: Match the architecture to the actual update frequency your business needs.
Pitfall 2: No fallback for connection drops
WebSocket connections drop. Mobile users switch networks. Corporate firewalls interfere. If your dashboard shows stale data without warning, users lose trust.
Fix: Always implement reconnection logic and display "last updated" timestamps. Show a visible indicator when the connection is down.
Pitfall 3: Rendering too much data
Loading 100,000 data points into a line chart. The browser freezes. D3.js can handle it technically, but the human eye cannot distinguish 100,000 points on a 1920px wide screen.
Fix: Aggregate data before rendering. Show daily summaries in the overview, drill down to hourly or per-transaction on demand.
Pitfall 4: No caching strategy
Every dashboard load queries the database for full historical data. With 50 concurrent users, that's 50 expensive queries hitting your production database simultaneously.
Fix: Cache dashboard data in Redis. Update cache incrementally when data changes. Serve from cache, never from direct database queries on page load.
Pitfall 5: Ignoring mobile
Dashboard looks great on a 27-inch monitor. Unusable on a phone. In Singapore, many managers and business owners check dashboards on mobile during commutes and meetings.
Fix: Design mobile-first. Use responsive chart components. Prioritize key metrics for small screens. Consider a separate mobile-optimised view.
Ready to build a real-time dashboard for your Singapore business? Let's talk. We'll assess your data sources, recommend the right architecture (often simpler than you think), and build a dashboard that gives you genuine visibility into your operations. Sometimes the answer is "a well-designed static dashboard with 5-minute refresh is all you need."
Frequently asked questions
Should I use WebSockets or polling for my business dashboard?
For most Singapore business dashboards, use Server-Sent Events (SSE) or polling with 5-10 second intervals. WebSockets add complexity (connection management, scaling, stateful infrastructure) that's only justified for sub-second update requirements like live trading platforms, real-time logistics tracking, or IoT monitoring. Polling with 5-second intervals is simple to implement, works with any backend, and provides near-real-time visibility for sales, operations, and management dashboards. SSE is the sweet spot: server pushes updates only when data changes, simpler than WebSockets, built into browsers.
Match architecture to actual update frequency needs, not theoretical ideals.
How much does a custom real-time dashboard cost in Singapore?
Small dashboard (5-8 metrics, 10-20 users): S$15,000-S$20,000 development plus S$80-S$150/month infrastructure. Medium dashboard (15-25 metrics, 50-100 users): S$25,000-S$35,000 development plus S$250-S$500/month infrastructure. Enterprise dashboard (50+ metrics, 500+ users): S$50,000-S$100,000 development plus S$800-S$2,000/month infrastructure. Custom dashboards may qualify for EDG grant funding (up to 50% coverage), reducing out-of-pocket costs significantly. Infrastructure hosted on AWS or Google Cloud Singapore region for PDPA compliance and low latency.
Budget development cost plus 12 months of infrastructure for total first-year cost.
What's the best visualization library for real-time dashboards in 2026?
Recharts for most React/Next.js business dashboards: clean API, good real-time performance, responsive by default, covers 90% of standard chart types (line, bar, area, pie). D3.js for complex custom visualizations (maps, network graphs, unique chart types) but overkill for standard business charts and requires significant development time. Chart.js as lightweight alternative for non-React projects or when bundle size matters (~60KB vs ~200KB for Recharts). Key tip: disable chart animations for real-time data updates to prevent jank and reduce CPU usage.
Use Recharts unless you need visualization types it cannot produce.
What database should I use for real-time dashboard data?
PostgreSQL as primary database (source of truth) with LISTEN/NOTIFY for event-driven updates, which works for most Singapore SME dashboards processing up to hundreds of events per minute. Redis as caching layer for pre-computed dashboard aggregations with sub-millisecond reads, pub/sub for distributing updates to connected clients, costs S$30-S$60/month on AWS ElastiCache. TimescaleDB (PostgreSQL extension) only if ingesting thousands of data points per second (IoT, financial tick data). The recommended pattern: write to PostgreSQL, trigger event, update Redis cache, serve dashboard from Redis.
PostgreSQL plus Redis covers 95% of business dashboard needs.
How do I secure real-time dashboard connections?
Four layers of security for real-time dashboards: (1) Always use WSS (WebSocket Secure) or HTTPS for SSE. Unsecured connections are blocked by browsers on HTTPS pages. (2) Authenticate during WebSocket handshake using JWT tokens. Verify before establishing connection, not after. (3) Implement role-based data filtering. Send each user only the metrics they're authorised to see. (4) Rate limit connections per IP (10 maximum) to prevent connection flooding. For Singapore PDPA compliance: encrypt data in transit, log access for audit trails, host on Singapore servers, and anonymise personal data in dashboard displays where possible.
Authenticate at connection time, filter data by role, and encrypt everything.
Can I host a real-time dashboard on a single server?
Yes. A single well-configured VPS (S$40-S$80/month, DigitalOcean or Vultr Singapore) can run the application server, PostgreSQL, and Redis together, handling up to 50 concurrent dashboard users with SSE or WebSocket connections comfortably. A single server handles 1,000+ concurrent WebSocket connections. Scale to multiple servers only when you actually need to. Premature scaling adds complexity without benefit. For redundancy, use managed database services (AWS RDS, S$100-S$200/month) even with a single application server, so database failures don't lose data.
Start with one server. Scale when traffic demands it, not before.
About &7: We build custom dashboards and web applications for Singapore businesses. Our dashboards connect to your existing data sources (databases, APIs, SaaS tools), display the metrics that actually matter, and update in real-time when that's genuinely needed. We'll recommend the simplest architecture that meets your requirements. Simpler systems are cheaper to build, easier to maintain, and more reliable in production.