When your dashboard lies under a filter
Averages without decomposition are just lies at lower resolution.
You’ve seen this. You open a B2B dashboard. There’s a row of headline KPIs at the top — revenue, MRR, active accounts, average contract value. Below them is a chart with a filter. You filter the chart down to a region, a customer segment, a project. The chart responds. The KPIs above it do not.
For the next five minutes, your dashboard is quietly lying to you. Not because the data is wrong. Because the headline numbers and the chart below them are reading from two different sources of truth.
This is the most common B2B dashboard bug
It happens because the chart owns its filter state internally and the KPIs above it pull from a separate, unfiltered query. Two components that look like they’re telling the same story are actually telling two stories. The user’s eye assumes coherence. The product silently breaks the assumption.
The bigger the dashboard, the more places this happens. A typical B2B SaaS overview I’ve seen will have a date-range filter at the top, a project filter on the chart, a status filter on a table, and a search box on a list — each owning its own state, none of them coordinating. The numbers shown are technically accurate. Together they describe a product that doesn’t exist.
The fix is one principle, applied four ways
The chart filter and the headline numbers must read from the same source of truth. Lift the filter state up to the page level. Make every component downstream of it read the same filtered dataset. The chart and the KPIs become two views of one thing instead of two things that happen to share a screen.
That principle, applied:
- Lift the filter state to the page. Not to a context provider, not to a global store — to the page component that owns both surfaces. The chart and the KPI row become controlled children that receive the filtered data as props.
- Switch sums to averages when filtering shrinks the set.A “total revenue” KPI for 50 customers and one for 3 customers should not look the same. Averages, with a sample size, do.
- Prefix every averaged number. A small
AVGeyebrow above each value tells the reader “this is a per-thing number, not a sum”. It costs you two characters and saves your customer success team a week of confused tickets. - Show the filter in the headline area. A one-line indicator pill —
● Filtered · 3 of 8 projects— lets the reader see, without scanning the chart, that they’re looking at a subset.
Decomposition is the part most teams skip
Even with all four fixes, the user is going to ask one more question: which projects? An average of 3 things hides 3 things. The honest move is to make the answer one hover away.
I add a small info button to each averaged KPI. Hovering reveals a tooltip listing every project in the current filter, with each one’s native-currency contribution to the average. The average has provenance. The tooltip is the receipt.
This is the principle worth remembering:
Averages without decomposition are just lies at lower resolution.
The bigger pattern
Every B2B dashboard has this shape: a few headline numbers, a breakdown chart, a detail table. If the headline can’t be recalculated from the breakdown, your dashboard has a structural bug — not a bug in any one component, a bug in how the surfaces relate to each other.
Filters expose it. Date ranges expose it. Tenancy switches expose it. The first time a user sees a filtered chart with unchanged KPIs above it, they don’t file a ticket — they just stop trusting the dashboard. Which is far worse than the bug itself.
How to know if your product has this
- Open your main dashboard. Apply a filter to any chart on the page.
- Watch the headline KPI row. Does anything change?
- If nothing changes, ask yourself: should it have? Are those KPIs a portfolio total (correctly unchanged) or a current-view summary (silently broken)?
If you’re not sure, your users aren’t sure either. That’s the bug.