Analytics Dashboard
Real-time data visualization platform with interactive charts and customizable widgets.
- Next.js
- D3.js
- Zustand
- Tailwind CSS
Overview
A real-time analytics platform processing millions of events per day, used by growth and operations teams to monitor product health, user behavior, and business metrics. The dashboard replaced a collection of disconnected spreadsheets and third-party tools that had become impossible to maintain.
The final system reduced time-to-insight for analysts from hours to seconds, and directly influenced three product decisions in its first month of deployment.
The Problem
The team had access to data — plenty of it — but no coherent way to surface it. Metrics lived in separate systems: some in a BI tool, some in custom SQL queries shared via Slack, some in third-party analytics platforms with inconsistent definitions. When stakeholders asked “how is feature X performing?”, the answer required stitching together four different data sources manually.
We needed a single, trustworthy interface for operational metrics that non-technical team members could configure themselves.
What I Built
- Customizable widget grid — users drag, resize, and configure metric widgets using a grid layout system built on
react-grid-layout, with per-user persistence - 10+ chart types built with D3.js, including time-series line charts with brush-to-zoom, funnel charts, cohort retention tables, and geo heatmaps
- Real-time updates via WebSocket subscriptions — metric values and charts update live with smooth GSAP transitions, no page reload required
- Global filter system — date range, user segment, and cohort filters propagate across all widgets simultaneously using a Zustand store
- Export pipeline — any widget can export its data as CSV or a shareable image using Canvas-based rendering
Technical Details
The biggest technical challenge was render performance at scale. D3.js gives you full control but no automatic optimizations — we had charts trying to render 50,000+ data points, which caused frame drops on lower-end machines.
The solution was a tiered rendering strategy: at coarse zoom levels, we use server-side aggregation to reduce point count; as users zoom in via the brush control, the client requests finer-grained data only for the visible time window. Combined with requestAnimationFrame scheduling for D3 updates and memoized scale computations, we hit consistent 60fps even on 4-year-old laptops.
State management was a deliberate choice for Zustand over Redux. The dashboard state has a relatively shallow shape — filters, widget configs, and realtime values — and Zustand’s minimal boilerplate kept the store logic readable without ceremony.