The problem it solves
The full telemetry dataset contains approximately 17,500 records covering one year of 3-minute interval data. Fetching all records at page load results in a 2–5 MB payload and a 2–5 second wait before the chart renders. During that time, the user sees a blank screen. Progressive loading reduces the time to first meaningful render to under 500 ms while still delivering the full dataset.Stage 1: Instant aggregated render
On tab load, the dashboard fetches daily aggregated data — one data point per day rather than one per 3-minute interval:loadingStage state transitions from 'initial' to 'streaming'.
Stage 2: Background streaming
After Stage 1, a seconduseEffect fires (triggered by loadingStage === 'streaming') and begins fetching raw records in chunks:
- New raw records are merged into state alongside the aggregated data
- Charts re-render progressively, replacing aggregated points with raw points for that date range
- A progress badge in the bottom-right corner shows completion percentage
- The UI remains fully interactive throughout — the user can change date ranges, select lamps, or apply filters while streaming continues
LOADING_CONFIG.STREAM_DELAY) prevents the browser from being overwhelmed.
When streaming is skipped
If the total record count exceeds 50,000, Stage 2 exits immediately and setsloadingStage = 'complete'. This prevents browser memory exhaustion on very large date ranges.
Chart downsampling
Even after full streaming, charts are limited to 300 data points (LOADING_CONFIG.MAX_CHART_POINTS). If more points are loaded, the chart step-samples — takes every Nth record — to stay within this limit. This prevents rendering lag without visibly affecting chart shape for trend analysis.
Performance results
The loading state machine
Three states govern the loading lifecycle, defined as theLoadingStage type in lib/types.ts:
Which tabs use progressive loading
Configuration constants
All tuneable values live inlib/constants.ts under LOADING_CONFIG: