Building a real-time dashboard for blockchain data is deceptively hard. The UI is the easy part; the hard part is deciding what “real-time” means on a probabilistic, reorg-prone, rate-limited network—and then engineering a pipeline that stays correct under load.
This post lays out a production-minded approach to app development for real-time blockchain dashboards, with concrete architecture choices and the tradeoffs you’ll actually face.
Start with the UX contract: what does “real-time” mean?
Before you pick tools, define your dashboard’s truth model. Blockchains don’t finalize instantly (and some never truly “finalize” the way users expect). A dashboard that shows a big green “confirmed” badge after 1 block is lying.
Define these states explicitly:
- Seen: mempool transaction observed (best-effort, can disappear).
- Included: in a block, but subject to reorg risk.
- Finalized: after N confirmations or protocol finality (varies by chain).
Then design the UI around it:
- Use distinct labels and colors for Included vs Finalized.
- Show confirmation count and estimated time-to-finality.
- When a reorg happens, reflect it (don’t silently “fix” charts).
A good dashboard is an operational instrument, not marketing.
Choose your ingestion source: node, provider, or indexer
You have three main options to obtain blockchain events:
Run your own nodes (e.g., Ethereum execution + consensus, Solana RPC, etc.).
- Pros: control, fewer surprises, can tune for your workload.
- Cons: ops overhead, storage growth, reorg handling is your job.
Use RPC providers (Alchemy, Infura, QuickNode, Blockdaemon).
- Pros: fastest path to production, good reliability.
- Cons: rate limits, occasional inconsistencies, cost at scale.
Use an indexing layer (The Graph, Subsquid, custom ETL, chain-specific indexers).
- Pros: query-friendly, built for derived views.
- Cons: “real-time” is bounded by index latency; custom logic may be harder.
Opinionated recommendation: use a provider + a lightweight custom indexer for dashboards that need sub-second updates and custom metrics. Relying solely on RPC calls from the frontend is a classic anti-pattern: it’s slow, expensive, and brittle.
Architect the pipeline: from chain events to live UI
A reliable real-time dashboard typically looks like this:
- Ingestion: subscribe to new blocks/logs/transactions via WebSocket (or poll as fallback).
- Normalization: parse and standardize events (addresses, topics, decimals, token metadata).
- Persistence: store raw events + derived tables.
- Aggregation: compute metrics (TPS, volume, TVL changes, liquidation events, MEV, etc.).
- Delivery: push updates to clients via WebSocket/SSE, plus REST for initial state.
Practical stack that works:
- Ingestion service: Node.js/TypeScript (ethers/viem) or Go/Rust.
- Queue/stream: Kafka, Redpanda, or Redis Streams (if smaller).
- DB: Postgres + TimescaleDB for time-series; ClickHouse for high-volume analytics.
- Cache: Redis for hot aggregates.
- Realtime API: WebSocket gateway (NestJS, Fastify, or dedicated gateway).
- Frontend: React + lightweight charting (ECharts, Visx, or TradingView for finance-grade charts).
The key is separating concerns: ingestion shouldn’t block on analytics queries; the UI shouldn’t hammer the chain.
Handle reorgs like an adult
Reorgs are the fastest way to destroy trust in your dashboard.
Implementation pattern:
- Store each event/tx with blockHash, blockNumber, and logIndex/txIndex.
- When you detect a new head, verify continuity against the previous head hash.
- If mismatch: walk back until a common ancestor is found, mark orphaned blocks as reverted, and roll back derived aggregates.
Two practical techniques:
- Append-only + compensation: keep immutable raw data and write “revert” records that negate aggregates.
- Materialized views with recompute window: for time buckets (e.g., 1m candles), recompute the last X minutes on every new block to absorb late arrivals and reorgs.
For many dashboards, a hybrid works well: compensation for per-event accuracy, recompute for charts.
Build derived metrics without melting your database
Dashboards are rarely about raw events; they’re about interpretation.
Examples:
- DEX dashboard: swaps per minute, volume by pair, price impact distribution.
- Lending dashboard: liquidations, borrow rates, utilization, health factor at risk.
- NFT dashboard: floor price, sales velocity, wash trade heuristics.
Avoid “querying your way to real-time.” Instead:
- Compute incrementally in a stream processor (Kafka Streams, Flink) or a worker service.
- Write aggregates into a table keyed by (metric, interval_start).
- Keep a Redis cache for the latest buckets.
For instance, if you need a “swaps per minute” chart:
- On each Swap event, increment a counter for the current minute bucket.
- Persist the updated bucket value.
- Push the updated point to connected clients.
This replaces expensive GROUP BY queries with O(1) updates.
Deliver real-time updates: SSE vs WebSockets
For dashboards, you generally want server-push.
- SSE (Server-Sent Events): simpler, one-way, great for streaming updates to many clients.
- WebSockets: bi-directional, useful if clients send filters/subscriptions dynamically.
Opinionated choice:
- Use REST for initial page load (fast caching, retries).
- Use SSE for live updates unless you truly need bi-directional control.
Also: implement topic-based subscriptions (e.g., “pair:ETH/USDC”, “protocol:Aave”, “chain:base”). Don’t broadcast everything to everyone.
Frontend performance: charts are your bottleneck
If your dashboard feels laggy, it’s often the charting layer, not the backend.
Practical tips:
- Batch updates (e.g., update UI at 250–500ms intervals).
- Downsample time-series for long ranges (1s resolution for 5 minutes, 1m for 24h, etc.).
- Use windowing for tables (react-virtualized / TanStack Virtual).
- Keep a clear separation between “live view” and “historical view.”
A common production pattern: show a live ticker for the last 30–120 seconds and a historical chart that updates every few seconds.
Observability and correctness checks
A real-time dashboard is an operational system. Treat it like one.
Minimum instrumentation:
- Ingestion lag: head block vs processed block.
- Reorg count and rollback depth.
- Provider error rates and reconnects.
- Event decode failures (ABI mismatches).
- End-to-end latency: block time → user-visible update.
Add correctness checks:
- Periodic reconciliation: compare your aggregates to a trusted source or recompute spot checks.
- Idempotency: dedupe by (txHash, logIndex) to survive reconnects.
Conclusion: real-time is a product feature, not a websocket
The difference between a toy blockchain dashboard and a production one isn’t React polish—it’s a rigorous definition of truth, robust reorg handling, incremental aggregation, and disciplined delivery to the UI.
If you do it right, you get a dashboard users can trade and operate on with confidence. If you do it wrong, you get a pretty screen that drifts from reality the moment the chain gets busy.
Build the pipeline first, define the correctness contract, then make it beautiful.