npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

uplot-plus

v0.1.0

Published

High-performance React charting library with multi-x-axis support

Readme

uPlot+

High-performance React charting library ripped off from uPlot. Might perform occasionally better than original uPlot in React context (or worse). Definitely easier to use in React context.

Live Demo (85+ examples)

Features

  • Canvas 2D rendering — no SVG or DOM elements for data visualization
  • Native React components — declarative <Chart>, <Series>, <Scale>, <Axis> API
  • Multi-x-axis support — multiple data groups with independent x-ranges on one chart
  • TypeScript-first — strict types, full type exports, no any
  • 7 path builders — linear, stepped, bars, monotone cubic, Catmull-Rom, points, candlestick
  • Interactive — wheel/touch zoom, drag-to-zoom, cursor snapping, series focus
  • Cursor sync — linked crosshairs and tooltips across multiple charts
  • Compact bundle — ~111KB (~30KB gzip), React 18+ peer dependency
  • Dual output — ES module + CommonJS

Installation

npm install uplot-plus

Peer dependencies:

npm install react react-dom

Quick Start

import { Chart, Scale, Series, Axis } from 'uplot-plus';

const data = [
  {
    x: [1, 2, 3, 4, 5],
    series: [
      [10, 25, 13, 30, 18],
      [5, 15, 20, 12, 28],
    ],
  },
];

function App() {
  return (
    <Chart width={800} height={400} data={data}>
      <Scale id="x" />
      <Scale id="y" />
      <Axis scale="x" label="X-Axis" />
      <Axis scale="y" label="Y-Axis" />
      <Series group={0} index={0} yScale="y" stroke="#e74c3c" width={2} label="Series A" />
      <Series group={0} index={1} yScale="y" stroke="#3498db" width={2} label="Series B" />
    </Chart>
  );
}

Components

| Component | Description | |-----------|-------------| | <Chart> | Root container — creates the canvas, manages the chart store | | <Scale> | Registers a scale (linear, log, asinh, ordinal) | | <Series> | Registers a data series with stroke, fill, path builder | | <Axis> | Renders an axis with ticks, labels, grid lines | | <Band> | Fills a region between two series | | <Legend> | Interactive legend with live cursor values, click-to-toggle | | <Tooltip> | Floating tooltip at cursor position, auto-flips at edges | | <ZoomRanger> | Overview mini-chart with draggable selection for zoom control | | <Timeline> | Horizontal lanes of colored event spans | | <Sparkline> | Compact inline chart for tables and dashboards (no axes, no interaction) | | <ResponsiveChart> | Auto-sizes to container via ResizeObserver | | <HLine> | Declarative horizontal line annotation | | <VLine> | Declarative vertical line annotation | | <Region> | Declarative shaded region annotation | | <AnnotationLabel> | Declarative text label at data coordinates |

Full props reference, usage examples, and demo links: docs/COMPONENTS.md

Data Model

Data is organized as groups, each with its own x-values:

import type { ChartData, XGroup } from 'uplot-plus';

// Single x-axis (most common)
const data: ChartData = [
  {
    x: [1, 2, 3, 4, 5],
    series: [
      [10, 20, 30, 40, 50],   // series 0
      [5, 15, 25, 35, 45],    // series 1
    ],
  },
];

// Multi x-axis — two groups with independent x-ranges
const multiData: ChartData = [
  { x: [0, 1, 2, 3], series: [[10, 20, 15, 25]] },
  { x: [0, 0.5, 1.5, 2.5, 3], series: [[8, 18, 22, 12, 30]] },
];

Each series is referenced by a (group, index) tuple — group is the index into the ChartData array, index is the index into that group's series array.

Null values in series arrays create gaps in the chart. Use spanGaps on <Series> to bridge them.

Path Builders

| Builder | Import | Use case | |---------|--------|----------| | linear() | linear | Line/area charts (default). Pixel-level decimation for large datasets | | stepped() | stepped | Step charts — step-after, step-before, or mid-step | | bars() | bars | Bar/column charts with configurable width, gaps, grouped bars | | monotoneCubic() | monotoneCubic | Smooth curves that preserve monotonicity (no overshoot) | | catmullRom() | catmullRom | Centripetal Catmull-Rom splines | | points() | points | Scatter plots — points only, no connecting lines | | drawCandlesticks() | drawCandlesticks | OHLC financial candlestick charts |

import { Series, bars } from 'uplot-plus';

<Series group={0} index={0} yScale="y" paths={bars()} stroke="#3498db" fill="#3498db80" />

Event Callbacks

React-idiomatic event handling — all callbacks receive resolved chart data (nearest point, data values, coordinates).

import { Chart, Scale, Series, Axis } from 'uplot-plus';

<Chart
  data={data} width={800} height={400}
  onClick={(info) => {
    if (info.point) {
      console.log(`Clicked series ${info.point.seriesIdx} at y=${info.point.yVal}`);
    }
  }}
  onContextMenu={(info) => {
    // Right-click with resolved nearest point
    showContextMenu(info.srcEvent, info.point);
  }}
  onDblClick={(info) => {
    // Return false to prevent default zoom reset
    return false;
  }}
  onSelect={(sel) => {
    // Intercept drag selection — fetch detail data instead of zooming
    fetchData(sel.ranges['x'].min, sel.ranges['x'].max);
    return false; // prevent zoom
  }}
  onScaleChange={(scaleId, min, max) => {
    console.log(`Scale ${scaleId} changed: [${min}, ${max}]`);
  }}
  onCursorMove={(info) => { /* fires on every mouse move */ }}
  onCursorLeave={() => { /* cursor left the plot */ }}
>
  <Scale id="x" />
  <Scale id="y" />
  <Axis scale="x" />
  <Axis scale="y" />
  <Series group={0} index={0} yScale="y" stroke="#e74c3c" width={2} label="Series A" />
</Chart>

Controlled Scales

Control zoom and pan declaratively through React state — no imperative refs needed:

import { useState, useCallback } from 'react';
import { Chart, Scale, Series, Axis } from 'uplot-plus';

function ZoomableChart({ data }) {
  const [xRange, setXRange] = useState<[number, number] | null>(null);

  const onScaleChange = useCallback((id: string, min: number, max: number) => {
    if (id === 'x') setXRange([min, max]);
  }, []);

  return (
    <>
      <button onClick={() => setXRange(null)}>Reset Zoom</button>
      <Chart data={data} width={800} height={400} onScaleChange={onScaleChange}>
        <Scale id="x"
          auto={xRange == null} min={xRange?.[0]} max={xRange?.[1]} />
        <Scale id="y" />
        {/* ... axes, series */}
      </Chart>
    </>
  );
}

Hooks

useChart()

Access the chart store from a child component of <Chart>. This is an advanced API for building custom chart sub-components that need direct store access — for most use cases, prefer event callbacks and controlled Scale props.

import { useChart } from 'uplot-plus';

function CustomControl() {
  const store = useChart();
  return <button onClick={() => store.toggleSeries(0, 0)}>Toggle</button>;
}

useDrawHook() / useCursorDrawHook()

Register custom Canvas 2D draw callbacks from child components. For most cases, prefer the onDraw / onCursorDraw props on <Chart> — these hooks are useful when building reusable chart sub-components.

import { useDrawHook } from 'uplot-plus';
import type { DrawCallback } from 'uplot-plus';

const onDraw: DrawCallback = (dc) => {
  dc.ctx.fillStyle = 'rgba(255,0,0,0.2)';
  dc.ctx.fillRect(dc.plotBox.left, dc.plotBox.top, dc.plotBox.width, dc.plotBox.height);
};

useStreamingData()

Sliding-window data management for real-time charts:

import { useStreamingData } from 'uplot-plus';

const { data, push, start, stop, fps } = useStreamingData(initialData, {
  window: 1000,   // keep last 1000 points
  batchSize: 10,  // push 10 points per tick
});

// In your tick callback:
push([newX], [newY1], [newY2]);

Axis Value Formatters

Pre-built formatters for common axis label patterns:

import { fmtCompact, fmtSuffix, fmtHourMin, fmtMonthName, fmtLabels } from 'uplot-plus';

<Axis scale="y" values={fmtCompact()} />           // 1.2K, 3.5M
<Axis scale="y" values={fmtSuffix('%')} />         // 42%
<Axis scale="y" values={fmtSuffix('°C', 1)} />    // 23.5°C
<Axis scale="x" values={fmtHourMin({ utc: true })} /> // 14:30
<Axis scale="x" values={fmtMonthName()} />         // Jan, Feb, ...
<Axis scale="x" values={fmtLabels(['Q1','Q2','Q3','Q4'])} />

Color Utilities

import { fadeGradient, withAlpha, palette } from 'uplot-plus';

// Gradient that fades from color to transparent (for area fills)
<Series fill={fadeGradient('#3498db')} />
<Series fill={fadeGradient('#e74c3c', 1.0, 0.2)} />

// Match fill to stroke with lower opacity
<Series stroke="#2980b9" fill={withAlpha('#2980b9', 0.1)} />

// Generate N distinct colors
const colors = palette(5); // 5 visually distinct HSL colors

Data Utilities

stackGroup

Computes stacked series values and generates band configs:

import { stackGroup, Band } from 'uplot-plus';

const raw = { x: [1, 2, 3], series: [[10, 20, 30], [5, 10, 15]] };
const { group, bands } = stackGroup(raw);

<Chart data={[group]}>
  {bands.map((b, i) => <Band key={i} {...b} />)}
</Chart>

alignData

Aligns data across multiple x-axes for multi-group charts.

Annotations

Declarative annotation components — place inside <Chart>:

import { HLine, VLine, Region, AnnotationLabel } from 'uplot-plus';

<Chart data={data}>
  {/* ... scales, axes, series */}
  <HLine value={65} yScale="y" stroke="#e74c3c" dash={[6, 4]} label="Threshold" />
  <VLine value={100} xScale="x" stroke="#8e44ad" dash={[4, 4]} />
  <Region yMin={40} yMax={60} yScale="y" fill="rgba(46,204,113,0.12)" />
  <AnnotationLabel x={50} y={65} text="Alert zone" fill="#e74c3c" />
</Chart>

Imperative helpers are available for advanced draw hooks that need programmatic control:

import { drawHLine, drawVLine, drawLabel, drawRegion } from 'uplot-plus';

Scale Utilities

For advanced draw hooks that need pixel conversions:

import { valToPos, posToVal } from 'uplot-plus';

const px = valToPos(dataValue, scale, dimension, offset);
const val = posToVal(pixelPos, scale, dimension, offset);

Development

npm run dev         # Start demo dev server (85+ examples)
npm run build       # Build library (ES + CJS to dist/)
npm run typecheck   # TypeScript strict check
npm run lint        # ESLint
npm run test        # Vitest test suite

License

MIT