Real-Time Data Dashboard: What It Actually Takes to Build One
A real-time data dashboard displays metrics that update as the underlying events happen, typically within seconds, rather than on a fixed refresh cycle measured in hours. The three things that make a dashboard genuinely real time are: a source that can push or be polled cheaply (an event stream, a change-data-capture feed, or an API with a webhook), a store that can answer aggregate queries in under a second at your data volume, and a front end that re-renders on new data instead of waiting for a page reload. Miss any one of those and you have a fast-looking dashboard sitting on stale numbers.
Most teams do not need all three. If your question is "how many orders today", a query that runs every 60 seconds against your production replica is real time enough, costs nothing, and takes an afternoon to build. Genuine sub-second streaming matters when a human or a system acts on the number within the same minute: fraud review queues, on-call incident boards, live event capacity, trading desks, delivery dispatch. Before you build anything, write down the decision the number drives and how long the person has to make it. That latency budget determines the entire architecture, and it is usually far looser than the phrase "real time" implies.
The Latency Budget Decides Everything
Here is the honest mapping between how fresh the data needs to be and what that costs you to build and run.
| Freshness needed | Typical approach | Build effort | Ongoing cost driver |
|---|---|---|---|
| Daily | Scheduled ETL into a warehouse, dashboard reads tables | Low | Warehouse compute per run |
| 5 to 60 minutes | Incremental batch, materialised views refreshed on a schedule | Low to medium | Refresh frequency |
| 30 seconds to 5 minutes | Polling a read replica or an API on an interval, cached aggregates | Medium | Query load on the source |
| 1 to 30 seconds | Change data capture into a fast analytical store, push to the client | High | Stream infrastructure, always-on |
| Sub-second | Event stream with in-memory aggregation, WebSocket delivery | Very high | Engineering time, on-call surface |
The jump between the third and fourth rows is where budgets die. Polling is a cron job and a cache. Streaming is a pipeline with its own failure modes: consumer lag, out-of-order events, exactly-once semantics, replay after an outage, schema evolution. If a five-minute-old number would produce the same decision as a five-second-old number, take the five minutes.
Polling Is Underrated, With One Caveat
A polling dashboard asks the source for fresh numbers on a timer. It is boring and it works. The caveat is that naive polling puts your dashboard's read load directly onto whatever it is polling, multiplied by every open browser tab.
Three fixes, in order of how much they buy you:
Poll server-side, not client-side. One process refreshes a cached result every 30 seconds. Every viewer reads the cache. Ten viewers and one viewer generate identical load on the database.
Poll incrementally. Instead of SELECT count(*) FROM orders WHERE created_at > today, keep a running aggregate and only scan rows newer than your last watermark. On a table with tens of millions of rows this is the difference between a 4-second scan and a 20ms one.
Poll the cheap thing first. Many APIs expose a lightweight endpoint that tells you whether anything changed: an ETag, a updated_since count, a webhook you can subscribe to instead. Hit that, and only pull the full payload when it moves.
If you are polling a third-party SaaS API rather than your own database, rate limits become the binding constraint before performance does. Most CRM and helpdesk APIs will not tolerate a 10-second poll across a wide set of records. Design for their limit, not your ideal.
Where the Simple Answer Breaks
Counts that disagree with each other. Your revenue tile reads from Stripe, your orders tile reads from your own database, and they are refreshed on different timers. At any given second they tell slightly different stories, and someone will screenshot the discrepancy and ask which one is broken. Neither is. Fix this by stamping every tile with the exact time its data was fetched, not a generic "live" badge. A visible "as of 14:32:07" ends more arguments than any amount of pipeline work.
Late-arriving events. A mobile client buffers offline and uploads an hour later. Your 15:00 revenue number changes at 16:00. If your dashboard renders immutable historical bars, they will silently shift and nobody will trust the chart again. Either window your aggregates so late data lands in the correct bucket and you accept retroactive movement, or freeze buckets after a grace period and route stragglers to a visible "adjustments" line.
The number is fine, the context is missing. A live error rate that has jumped from 0.2% to 3% tells you something is wrong. It does not tell you that a deploy went out four minutes ago, that a customer already complained in a shared Slack channel, or that support has three tickets open on the same symptom. This is the most common failure of real-time dashboards: the chart is correct and the responder still has to open five tabs to understand it.
Dashboards nobody looks at. A dashboard is a pull mechanism. It only works if someone chooses to look at it during the window where the number matters. For genuinely urgent conditions, an alert that pushes into the channel where people already are will beat any dashboard, and the dashboard becomes the place you go after the alert to understand what happened.
A Worked Example: Live Support Queue
Say you want an operations screen showing the current support load. Concretely you want open tickets by priority, median time to first response for the last hour, tickets breaching SLA in the next 30 minutes, and which agents are at capacity.
The naive build polls your helpdesk API every 10 seconds for all open tickets, computes everything client-side, and hits a rate limit by lunchtime.
The build that survives contact with production looks like this. Subscribe to ticket webhooks so creation, assignment and status changes push to you within a second or two. Maintain a small local table of currently open tickets, updated by those webhooks. Reconcile that table against a full API pull every 10 minutes to catch anything the webhooks dropped, because they do drop. Compute the aggregates against the local table, which now costs nothing. Push updates to the browser over server-sent events. Total data freshness: a couple of seconds for changes, with a 10-minute worst case for a missed event, which for this decision is entirely acceptable.
Note the pattern: pushed updates for freshness, periodic reconciliation for correctness. Almost every reliable real-time dashboard is that pair. Systems that rely on the stream alone eventually drift, and the drift is invisible until someone checks by hand.
What to Put On the Screen
Real-time dashboards fail on design as often as on plumbing.
Show a change indicator, not just a value. A number alone gives the viewer nothing to react to. Compared to the same hour yesterday, or to a rolling baseline, tells them whether to care.
Cap the tile count at what fits on one screen without scrolling. If it needs a scrollbar it is a report, and reports do not need to be real time.
Never animate a number that updates every second. It reads as motion, the eye tracks it, and it stops anyone reading anything else on the page. Update in place, quietly.
Make the failure state loud. If the feed has been stale for three minutes, the entire panel should say so. A frozen dashboard that looks healthy is worse than no dashboard, because people continue to trust it.
Give every tile a drill-down. The value of a live number is almost always in the rows behind it, and asking someone to go write a query defeats the point of the screen.
Real-Time Analytics Services: Build or Buy
If you are choosing infrastructure, the shape of the decision is straightforward.
Managed stream processing plus a columnar store built for it, ClickHouse, Apache Pinot, Apache Druid, Tinybird and similar, is the right answer when you have genuine high-cardinality event volume and sub-second requirements. Expect real engineering investment.
Your existing warehouse with incremental models and a scheduled refresh, Snowflake, BigQuery, Databricks with dbt on a short interval, is right when minutes are acceptable. It reuses the modelling you already have and adds no new operational surface.
A read replica plus a cache is right for most operational dashboards on transactional data. It is unglamorous and it usually wins on total cost of ownership.
The mistake is picking streaming infrastructure for a use case that a 60-second cron would have solved, then carrying that operational weight for years.
When the Evidence Is Not in a Database
The hardest part of most real-time dashboards is not the numbers, it is that the explanation lives somewhere the dashboard cannot see. Warehouses and BI tools connect to databases and modelled sources, so a spike in refund volume can be charted precisely while the reason for it, a bug report in Linear, a thread in a shared Slack channel, an email from the payment processor, sits entirely outside what any of them can query.
That gap is what Skopx works on. It connects to nearly 1,000 SaaS tools plus databases directly, so you can ask a question in chat and get an answer that spans a live SQL query and a conversation that happened this morning, with citations to both. If you want a live operational console rather than an ad-hoc question, describing it in a sentence produces one that reads across those same connections and can take an action behind a button that a person clicks and confirms: see Internal Apps.
For the dashboard itself, though, the advice stands on its own. Write down the decision, write down how long the person has to make it, and build the cheapest thing that meets that budget. Most teams discover the budget is minutes, not milliseconds.
Skopx Team
The Skopx engineering and product team