Balaji Arumugam
Back to Blog

Building Production KPI Dashboards in React That Stakeholders Actually Use

B

Balaji Arumugam

Software Engineer

Mar 10, 20269 min readFrontend
Contentsexpand_more

Every manufacturing facility is drowning in data — OEE percentages, downtime reasons, defect counts, throughput rates, shift comparisons. Building a dashboard that displays all of this is the easy part. Building one that a shift supervisor glances at and immediately knows what needs attention — that's the engineering challenge nobody warns you about.

The Real Challenge Isn't the Data

When I started building the production analytics dashboard for the Amway India MES, my first instinct was to surface everything. There were machines generating calibration logs, quality inspection results, downtime event reasons, and KPI rollups every few minutes. The first prototype showed all of it — and was immediately useless. The operations team had to hunt for the single number that mattered right now. The real job was editorial, not technical: deciding what to show, what to hide, and what to escalate. A Pareto chart of downtime reasons is infinitely more actionable than a table of 40 event types sorted by timestamp.

Structuring Multi-chart Layouts Without Coupling

A dashboard that starts with three charts rarely stays at three. Feature requests compound: drill-down on a bar segment, date range pickers that affect all charts simultaneously, per-line filtering. Without intentional architecture, each chart becomes a black box that knows too much about its siblings. The approach that scaled cleanly was treating each chart as a pure display component — it receives data and config props, nothing else. All filtering, date selection, and shared state lived in a Zustand store one level up. Charts subscribed only to the slice of state they needed, which meant adding a new chart never touched existing ones.

Chart Library Trade-offs: ChartJS, Apex Charts, Recharts

The MES dashboard ended up using all three libraries, which sounds like a mistake until you see why. ChartJS handled the straightforward bar and line charts — fast, small bundle, trivial to configure for standard shapes. Apex Charts earned its place for the complex interactive charts: timeline series with zoom, grouped bar comparisons across shifts, and Pareto charts with dual axes. Its built-in interactivity saved weeks of custom implementation. Recharts came in for the React-idiomatic use cases where we needed fine-grained control over rendering — specifically the custom tooltip components that had to match the production floor's language precisely. Mixing libraries added bundle overhead, but each earned its presence.

Real-time Updates Without Killing Performance

React Query's polling made the live KPI tiles straightforward — a staleTime of zero and a refetchInterval tuned per chart type. High-priority tiles like current shift throughput polled every 30 seconds. Trend charts covering the past 7 days polled every 5 minutes. The mistake that caused early performance problems was triggering a full chart redraw on every poll, even when the incoming data was identical. Memoizing the chart data transform with useMemo and comparing the result against the previous render before committing to a re-render eliminated most of the unnecessary computation. On the older terminals deployed on the production floor, this difference was visible.

Designing for the Production Floor, Not the Boardroom

The final piece — and the one that took the most iteration — was making the dashboard legible to operators who were checking it from 2 meters away, under fluorescent lighting, between line tasks. Large typography for the primary KPI number. Red/amber/green color encoding that didn't require reading a label. Progressive disclosure: the summary view showed status, the drill-down showed cause. Every chart title answered a question, not described a metric. 'Why did Line 3 lose 40 minutes?' outperforms 'Downtime by Category — Line 3' in every usability test.

  • Chart Library Selection: ChartJS for standard shapes, Apex Charts for interactive complexity, Recharts for React-native composability — each with a clear role.
  • Zustand for Shared Filter State: Decoupling chart components from shared state prevents cascading complexity as the dashboard grows.
  • Memoized Data Transforms: Comparing transformed data before triggering re-renders eliminated visible lag on older floor terminals.

Key Takeaway

The technical decisions — chart library, polling interval, state architecture — matter far less than the editorial decision: what should a person on the production floor see in the first 3 seconds? Answer that question first, then build the system to support it.

A dashboard that shows everything tells you nothing. The hardest design decision is always what to leave out.