Line Chart

Baseline cartesian chart for tracking change over ordered dimensions like time or sequence.

npm install @plocks/charts

Basics

Simple random data line chart with title.

Loading demo…

import { LineChart } from '@plocks/charts'; import { SERIES } from './data'; export function Demo() { return ( <LineChart // title="Monthly active customers" // subtitle="FY25" h={320} series={SERIES} xAxis={{ show: true, title: 'Month', labelFormatter: (value) => `M${value}`, }} yAxis={{ show: true, title: 'Customers (thousands)', labelFormatter: (value) => `${value}`, }} grid={{ show: true, style: 'dashed' }} legend={{ show: true, position: 'bottom' }} tooltip={{ show: true, formatter: (point) => `${point.y}k customers in month ${point.x}`, }} enableCrosshair multiTooltip liveTooltip enablePanZoom zoomMode="x" minZoom={0.3} /> ); }

Smooth Area

Smoothed dual-series line chart with gradient fill and custom legend.

Loading demo…

import { LineChart } from '@plocks/charts'; import { SERIES } from './data'; export function Demo() { return ( <LineChart title="Revenue trajectory" subtitle="Smoothed forecast vs. actuals" h={320} series={SERIES} smooth fill showPoints={false} lineThickness={3} fillOpacity={0.28} enableCrosshair multiTooltip liveTooltip enablePanZoom zoomMode="x" minZoom={0.35} legend={{ show: true, position: 'top', align: 'center' }} grid={{ show: true, style: 'dashed' }} xAxis={{ show: true, title: 'Month', labelFormatter: (value) => `M${value}`, }} yAxis={{ show: true, title: 'Revenue (USD thousands)', labelFormatter: (value) => `$${Math.round(value)}`, }} tooltip={{ show: true, formatter: (point) => `$${point.y.toLocaleString()}k in month ${point.x}`, }} annotations={[ { id: 'midyear-target', shape: 'vertical-line', x: 6, label: 'Mid-year target', color: '#6366F1', dashArray: [6, 6], }, ]} /> ); }

Time Series

Time-series interaction example with brush zoom and custom tooltip formatting.

Loading demo…

import { LineChart } from '@plocks/charts'; import { SERIES, formatter } from './data'; export function Demo() { return ( <LineChart title="Web analytics" subtitle="Sessions and goals over time" h={360} series={SERIES} xScaleType="time" enableCrosshair multiTooltip liveTooltip enablePanZoom enableBrushZoom zoomMode="x" minZoom={0.25} legend={{ show: true, position: 'bottom', align: 'center' }} grid={{ show: true, style: 'dotted' }} xAxis={{ show: true, title: 'Month', labelFormatter: (value) => formatter.format(new Date(value)), }} yAxis={{ show: true, title: 'Count', labelFormatter: (value) => value.toLocaleString(), }} tooltip={{ show: true, formatter: (point) => { const label = formatter.format(new Date(point.x)); return `${label}: ${point.y.toLocaleString()} ${point.id === 'goal-completions' ? 'goals' : 'sessions'}`; }, }} annotations={[ { id: 'holiday-campaign', shape: 'range', x1: Date.UTC(2024, 10, 1), x2: Date.UTC(2024, 11, 31), label: 'Holiday campaign', color: '#0EA5E9', backgroundColor: 'rgba(14,165,233,0.12)', }, ]} /> ); }

Gradient area fill

Key settings
  • fillColor accepts a gradient as well as a color. A plain color fades from fillOpacity at the line to transparent at the baseline; a gradient is used exactly as given.
  • extent: 'plot' spans one gradient across the whole plot, so peaks reach the deep end while quiet hours stay pale. Set fillColor on an individual series to give each area its own gradient.

Loading demo…

import { LineChart } from '@plocks/charts'; import { CONCURRENT_VIEWERS } from './data'; export function Demo() { return ( <LineChart title="Concurrent viewers" subtitle="One gradient spans the plot: the evening peak reaches the deepest blue" h={320} data={CONCURRENT_VIEWERS} smooth fill showPoints={false} lineThickness={2.5} fillColor={{ angle: 90, extent: 'plot', stops: [ { offset: 0, color: '#1c5cab', opacity: 0.9 }, { offset: 1, color: '#86b6ef', opacity: 0.12 }, ], }} grid={{ show: true }} xAxis={{ show: true, title: 'Hour', labelFormatter: (value) => `${String(Math.round(value)).padStart(2, '0')}:00`, }} yAxis={{ show: true, title: 'Viewers (thousands)', labelFormatter: (value) => `${Math.round(value)}k`, }} tooltip={{ show: true, formatter: (point) => `${point.y}k viewers at ${String(point.x).padStart(2, '0')}:00`, }} /> ); }

Zoom & pan

Interactive zoom and pan on desktop web. Scroll the wheel over the plot to zoom, drag to pan, hold Shift and drag a box to zoom into a region, and double-click to reset.
Note that wheel zoom requires both enableWheelZoom and enablePanZoom — the wheel handler is a no-op when panning is disabled. zoomMode="x" constrains zooming to the time axis.

Loading demo…

import { LineChart } from '@plocks/charts'; import { SERIES } from './data'; export function Demo() { return ( <LineChart title="Product engagement" subtitle="Scroll to zoom · drag to pan · Shift-drag to box-zoom · double-click to reset" h={340} series={SERIES} xAxis={{ show: true, title: 'Week', labelFormatter: (value) => `W${value}` }} yAxis={{ show: true, title: 'Count' }} grid={{ show: true, style: 'dashed' }} legend={{ show: true, position: 'bottom' }} tooltip={{ show: true }} enableCrosshair multiTooltip liveTooltip // Zoom & pan gestures (desktop web): enablePanZoom // drag to pan (and gates wheel zoom) enableWheelZoom // scroll wheel to zoom enableBrushZoom // Shift + drag a box to zoom into it resetOnDoubleTap // double-click to reset the view zoomMode="x" // zoom the x-axis (time) only minZoom={0.15} /> ); }

Arr Progress Forecast

Story focus
  • Compares current ARR performance for each GTM region against the latest forecast trajectory.

  • Highlights the forward-looking window so teams can inspect upside or downside risk.

  • Uses matching dashed overlays to keep forecast lines aligned with their actual counterparts.

Key settings
  • Provides paired solid and dashed series per region, sharing colors for quick comparison.

  • Applies a range annotation to tint the forecast horizon on the right side of the chart.

  • Enables multi-series tooltip formatting with ARR values expressed in millions.

Loading demo…

import { LineChart } from '@plocks/charts'; import { FORECAST_END, FORECAST_START, MONTH_LABELS, SERIES } from './data'; export function Demo() { return ( <LineChart title="ARR Progression vs. Forecast" subtitle="GTM regions actualized ARR with forward-looking plans" h={440} series={SERIES} smooth showPoints pointSize={5} grid={{ show: true, style: 'dashed' }} legend={{ show: true, position: 'bottom', align: 'center' }} tooltip={{ show: true, formatter: (point) => { const month = MONTH_LABELS[Math.round(point.x)]; const region = point.data?.region?.toUpperCase?.() ?? 'Region'; const label = point.data?.type === 'forecast' ? 'Forecast' : 'Actual'; return `${month} • ${region} ${label}: $${point.y.toFixed(0)}M ARR`; }, }} annotations={[ { id: 'forecast-window', shape: 'range', x1: FORECAST_START, x2: FORECAST_END, label: 'Forecast window', backgroundColor: '#2563eb1a', textColor: '#1f2937', }, ]} xAxis={{ show: true, title: 'Timeline', labelFormatter: (value: number) => MONTH_LABELS[Math.round(value)] ?? `M${Math.round(value) + 1}`, }} yAxis={{ show: true, title: 'ARR ($M)', labelFormatter: (value: number) => `$${Math.round(value)}M`, }} enableCrosshair multiTooltip liveTooltip /> ); }

Cohort Retention Curves

Story focus
  • Visualizes how recent signup cohorts retain through the first 120 days of product usage.

  • Calls out steady improvement quarter-over-quarter with the newest cohort holding above 50%.

  • Reinforces the retention target so growth teams can spot which cohorts beat the goal.

Key settings
  • Keeps straight segments (smooth={false}) to preserve milestone-to-milestone retention steps.
  • Adds a horizontal annotation line at the 45% goal for quick benchmarking.

  • Expands tooltips to include cohort and milestone context in the retention readout.

Loading demo…

import { LineChart } from '@plocks/charts'; import { MILESTONES, SERIES, TARGET_RETENTION } from './data'; export function Demo() { return ( <LineChart title="Cohort Retention Across Milestones" subtitle="Weekly retention milestones by signup quarter" h={440} series={SERIES} smooth={false} showPoints grid={{ show: true, style: 'dotted' }} legend={{ show: true, position: 'bottom', align: 'center' }} tooltip={{ show: true, formatter: (point) => { const milestone = point.data?.milestone ?? `Milestone ${point.x + 1}`; const cohort = point.data?.cohort ?? 'Cohort'; return `${cohort} • ${milestone}: ${point.y.toFixed(0)}% retained`; }, }} annotations={[ { id: 'target-retention', shape: 'horizontal-line', y: TARGET_RETENTION, label: 'Target 45% Retention', color: '#0EA5E9', textColor: '#0F172A', }, ]} xAxis={{ show: true, title: 'Customer milestone', labelFormatter: (value: number) => MILESTONES[Math.round(value)] ?? `Step ${Math.round(value) + 1}`, }} yAxis={{ show: true, title: 'Percent of original cohort', labelFormatter: (value: number) => `${Math.round(value)}%`, }} enableCrosshair liveTooltip /> ); }

Energy Consumption Portfolio

Story focus
  • Benchmarks energy usage across three global offices as efficiency projects roll out.

  • Highlights the hot-weather season where cooling demand spikes so facilities can react.

  • Tracks progress against the 360 MWh portfolio target while flagging retrofit milestones.

Key settings
  • Demonstrates per-series smoothing choices (smooth on each series) while the chart default remains unsmoothed.
  • Utilizes range, vertical, and horizontal annotations to spotlight seasonal context and program milestones.

  • Formats tooltip readouts with building names and month labels for facilities reporting.

Loading demo…

import { LineChart } from '@plocks/charts'; import { COOLING_SEASON, MONTHS, PORTFOLIO_TARGET, SERIES } from './data'; export function Demo() { return ( <LineChart title="Energy Consumption Across Office Portfolio" subtitle="Monthly MWh usage benchmarking against 360 MWh target" h={440} series={SERIES} smooth={false} grid={{ show: true, style: 'dashed' }} legend={{ show: true, position: 'bottom', align: 'center' }} tooltip={{ show: true, formatter: (point) => { const month = point.data?.month ?? `Month ${point.x + 1}`; const building = point.data?.building ?? 'Site'; return `${building} • ${month}: ${point.y.toFixed(0)} MWh`; }, }} annotations={[ { id: 'cooling-season', shape: 'range', x1: COOLING_SEASON.start, x2: COOLING_SEASON.end, label: 'Cooling season monitoring', backgroundColor: '#0ea5e91a', textColor: '#0C4A6E', }, { id: 'target-line', shape: 'horizontal-line', y: PORTFOLIO_TARGET, label: 'Target 360 MWh', color: '#16A34A', textColor: '#14532D', }, { id: 'retrofit-complete', shape: 'vertical-line', x: 3, label: 'LED retrofit complete', color: '#10B981', textColor: '#064E3B', }, ]} xAxis={{ show: true, title: '2024 calendar', labelFormatter: (value: number) => MONTHS[Math.round(value)] ?? `M${Math.round(value) + 1}`, }} yAxis={{ show: true, title: 'Energy consumed (MWh)', labelFormatter: (value: number) => `${Math.round(value)} MWh`, }} enableCrosshair multiTooltip liveTooltip /> ); }

Incident Volume Moving Average

Story focus
  • Shows how daily incident intake surged around a major outage and eventually normalized.

  • Overlays 7-day and 14-day moving averages so reliability leads can compare short- vs. medium-term trendlines.

  • Highlights a stabilization period driven by the SRE playbook after the spike.

Key settings
  • Draws dashed and dotted overlay series using the new per-series lineStyle support.
  • Adds vertical and range annotations to call out root cause analysis and remediation windows.

  • Turns on multi-series tooltips so moving averages and raw volume can be read together.

Loading demo…

import { LineChart } from '@plocks/charts'; import { DAYS, MAJOR_OUTAGE_DAY, SERIES, STABILIZATION_END, STABILIZATION_START } from './data'; export function Demo() { return ( <LineChart title="Incident Volume with Moving Averages" subtitle="SRE daily incident intake and trailing trends" h={440} series={SERIES} smooth grid={{ show: true, style: 'solid' }} legend={{ show: true, position: 'bottom', align: 'center' }} tooltip={{ show: true, formatter: (point) => { const label = point.data?.window ? `${point.data.window}-day avg` : 'Incidents'; const day = DAYS[Math.round(point.x)]; return `${day} • ${label}: ${point.y.toFixed(1)} incidents`; }, }} annotations={[ { id: 'major-outage', shape: 'vertical-line', x: MAJOR_OUTAGE_DAY - 1, label: 'Major outage root cause', color: '#DC2626', textColor: '#0F172A', }, { id: 'stabilization-window', shape: 'range', x1: STABILIZATION_START, x2: STABILIZATION_END, label: 'Stabilization playbook', backgroundColor: '#22c55e22', textColor: '#14532d', }, ]} xAxis={{ show: true, title: 'Rolling 30-day window', labelFormatter: (value: number) => DAYS[Math.round(value)] ?? `Day ${Math.round(value) + 1}`, }} yAxis={{ show: true, title: 'Incident count', labelFormatter: (value: number) => `${Math.round(value)}`, }} enableCrosshair multiTooltip liveTooltip /> ); }

Nps Trend Release Markers

Story focus
  • Tracks the steady climb in NPS as successive product experiences ship throughout the year.

  • Annotates each launch so teams can correlate release timing with sentiment jumps.

  • Keeps a green performance line so customer orgs know when the brand clears the NPS target.

Key settings
  • Enables area fill to spotlight the magnitude of the NPS climb across months.

  • Uses vertical annotations with labels to mark major releases on the timeline.

  • Adds a horizontal annotation at the 55-point goal for immediate benchmarking.

Loading demo…

import { LineChart } from '@plocks/charts'; import { MONTHS, RELEASE_MARKERS, SERIES } from './data'; export function Demo() { return ( <LineChart title="NPS Trend with Product Releases" subtitle="Quarterly sentiment lift alongside major launches" h={420} series={SERIES} smooth fill grid={{ show: true, style: 'dashed' }} legend={{ show: false }} tooltip={{ show: true, formatter: (point) => { const month = point.data?.month ?? `Month ${point.x + 1}`; return `${month} NPS: ${point.y.toFixed(0)}`; }, }} annotations={[ ...RELEASE_MARKERS.map((marker) => ({ id: marker.id, shape: 'vertical-line' as const, x: marker.x, label: marker.label, color: '#6366F1', textColor: '#312E81', backgroundColor: '#E0E7FF', })), { id: 'nps-target', shape: 'horizontal-line', y: 55, label: 'Target 55 NPS', color: '#16A34A', textColor: '#0F172A', }, ]} xAxis={{ show: true, title: '2024 timeline', labelFormatter: (value: number) => MONTHS[Math.round(value)] ?? `M${Math.round(value) + 1}`, }} yAxis={{ show: true, title: 'Net Promoter Score', labelFormatter: (value: number) => `${Math.round(value)}`, }} enableCrosshair liveTooltip /> ); }
Scan to open this page on your phoneplocks.dev/charts/LineChart