Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 44 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
# chartgpu-react
<p align="center" style="margin-bottom:0; margin-top:20px;">
<img src="docs/assets/chartgpu.png" alt="ChartGPU" width="400">
</p>

<p align="center">
React bindings for <a href="https://github.com/ChartGPU/ChartGPU">ChartGPU</a> — WebGPU-powered charting for high-performance data visualization.
React bindings for <a href="https://github.com/ChartGPU/ChartGPU">ChartGPU</a> — The fastest open-source charting library — 50M points at 60 FPS.
</p>

<div align="center">

[<img src="docs/assets/powered-by-webgpu.svg" alt="Powered by WebGPU" height="28" />](https://forthebadge.com)
[![Documentation](https://img.shields.io/badge/Documentation-Getting%20Started-blue?style=for-the-badge)](https://github.com/chartgpu/chartgpu-react/blob/main/docs/GETTING_STARTED.md)
[![API Reference](https://img.shields.io/badge/API-Reference-blue?style=for-the-badge)](https://github.com/chartgpu/chartgpu-react/blob/main/docs/API.md)
[![Examples](https://img.shields.io/badge/Examples-Run%20Locally-blue?style=for-the-badge)](https://github.com/chartgpu/chartgpu-react/tree/main/examples)

[![npm version](https://img.shields.io/npm/v/chartgpu-react?style=for-the-badge&color=blue)](https://www.npmjs.com/package/chartgpu-react)
[![NPM Downloads](https://img.shields.io/npm/dm/chartgpu-react?style=for-the-badge&color=%2368cc49)](https://www.npmjs.com/package/chartgpu-react)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://www.npmjs.com/package/chartgpu-react)

[<img src="https://hackerbadge.now.sh/api?id=46706528" alt="Featured on Hacker News" height="30" />](https://news.ycombinator.com/item?id=46706528)

[<img src="https://awesome.re/mentioned-badge.svg" alt="Featured in Awesome WebGPU" style="height: 30px;" />](https://github.com/mikbry/awesome-webgpu)


</div>

`chartgpu-react` is a **thin React + TypeScript wrapper** around the [`chartgpu`](https://www.npmjs.com/package/chartgpu) core library.
`chartgpu-react` is a **thin React + TypeScript wrapper** around the [`@chartgpu/chartgpu`](https://www.npmjs.com/package/@chartgpu/chartgpu) core library.

## Highlights

- **`ChartGPU` component (recommended)**: async create/dispose lifecycle + debounced `ResizeObserver` sizing
- **Event props**: `onClick`, `onCrosshairMove`, `onZoomChange`, etc.
- **Imperative `ref` API**: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData`, `setOption`)
- **Hooks**: `useChartGPU(...)`, `useConnectCharts(...)`
- **Helper re-exports (from `chartgpu`)**: `createChart`, `connectCharts`, `createAnnotationAuthoring`
- **Imperative `ref` API**: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData`, `setOption`, `setZoomRange`, `setInteractionX`, `getInteractionX`, `hitTest`)
- **Hooks**: `useChartGPU(...)`, `useConnectCharts(..., syncOptions?)`
- **Helper re-exports (from `@chartgpu/chartgpu`)**: `createChart`, `connectCharts`, `createAnnotationAuthoring`

## Quick start

Expand Down Expand Up @@ -60,7 +67,7 @@ function MyChart() {
## Installation

```bash
npm install chartgpu-react chartgpu react react-dom
npm install chartgpu-react @chartgpu/chartgpu react react-dom
```

### Requirements
Expand All @@ -79,20 +86,20 @@ Check browser compatibility at [caniuse.com/webgpu](https://caniuse.com/webgpu).
- lifecycle management (async create + dispose)
- `ResizeObserver` resize (debounced)
- event props: `onClick`, `onCrosshairMove`, `onZoomChange`, etc.
- imperative `ref` API: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData`, `setOption`)
- imperative `ref` API: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData`, `setOption`, `setZoomRange`, `setInteractionX`, `getInteractionX`, `hitTest`)
- **Hooks**
- `useChartGPU(containerRef, options)` — create/manage a chart instance
- `useConnectCharts([chartA, chartB, ...])` — sync crosshair/interaction-x across charts
- `useConnectCharts([chartA, chartB, ...], syncOptions?)` — sync crosshair/interaction-x (and optionally zoom) across charts
- **Deprecated**
- `ChartGPUChart` (legacy adapter; use `ChartGPU` instead)
- **Helper re-exports** (from peer dependency `chartgpu`)
- **Helper re-exports** (from peer dependency `@chartgpu/chartgpu`)
- `createChart`, `connectCharts`, `createAnnotationAuthoring`

For details, start with the [API reference](./docs/API.md).

## Feature snippets (ChartGPU core)

These snippets use helpers and events from the `chartgpu` core library (peer dependency of `chartgpu-react`).
These snippets use helpers and events from the `@chartgpu/chartgpu` core library (peer dependency of `chartgpu-react`).

### Crosshair / interaction X (`'crosshairMove'`)

Expand All @@ -112,16 +119,19 @@ import type { ChartGPUCrosshairMovePayload } from 'chartgpu-react';
### Connect charts (sync crosshair/tooltip)

```tsx
import { connectCharts } from 'chartgpu';
import { connectCharts } from 'chartgpu-react';

// When you have two ChartGPUInstance objects:
const disconnect = connectCharts([chartA, chartB]);

// With zoom sync:
// const disconnect = connectCharts([chartA, chartB], { syncZoom: true });

// Later:
disconnect();
```

If you prefer a hook-driven approach, you can use `onReady` (or `useChartGPU`) to capture instances, then call `connectCharts(...)` once both are available.
If you prefer a hook-driven approach, you can use `onReady` (or `useChartGPU`) to capture instances, then call `useConnectCharts(...)` once both are available.

### Annotation authoring UI (`createAnnotationAuthoring`)

Expand Down Expand Up @@ -158,7 +168,7 @@ function AnnotationAuthoringExample() {
import { useEffect, useRef } from 'react';
import { ChartGPU } from 'chartgpu-react';
import type { ChartGPUHandle, ChartGPUOptions } from 'chartgpu-react';
import type { OHLCDataPoint } from 'chartgpu';
import type { OHLCDataPoint } from 'chartgpu-react';

function CandlestickStreaming() {
const ref = useRef<ChartGPUHandle>(null);
Expand Down Expand Up @@ -232,28 +242,28 @@ The dev server will start at `http://localhost:3000` and open the examples page

### Local development with linked ChartGPU

To develop `chartgpu-react` against a local version of the `chartgpu` package (useful for testing changes across both repositories):
To develop `chartgpu-react` against a local version of the `@chartgpu/chartgpu` package (useful for testing changes across both repositories):

```bash
# 1. Link the chartgpu package from the sibling repo
# 1. Link the @chartgpu/chartgpu package from the sibling repo
cd ../chart-gpu
npm link

# 2. Link chartgpu into this project
# 2. Link @chartgpu/chartgpu into this project
cd ../chartgpu-react
npm link chartgpu
npm link @chartgpu/chartgpu

# 3. Build and run - will use the linked local package
npm run build
npm run dev
```

**Note:** After linking, `npm run build` and `npm run dev` will resolve imports to your local `chartgpu` package instead of the published version. This allows you to test changes in both repos simultaneously.
**Note:** After linking, `npm run build` and `npm run dev` will resolve imports to your local `@chartgpu/chartgpu` package instead of the published version. This allows you to test changes in both repos simultaneously.

To unlink and return to the published package:

```bash
npm unlink chartgpu
npm unlink @chartgpu/chartgpu
npm install
```

Expand All @@ -267,13 +277,25 @@ import type {
ChartGPUOptions,
ChartGPUEventPayload,
ChartGPUCrosshairMovePayload,
ChartGPUZoomRangeChangePayload,
ChartGPUHitTestResult,
ChartGPUHitTestMatch,
ChartSyncOptions,
AreaSeriesConfig,
LineSeriesConfig,
BarSeriesConfig,
PieSeriesConfig,
ScatterSeriesConfig,
SeriesConfig,
LineStyleConfig,
AreaStyleConfig,
DataPoint,
LegendConfig,
LegendPosition,
AnimationConfig,
TooltipConfig,
TooltipParams,
PerformanceMetrics,
} from 'chartgpu-react';
```

Expand All @@ -293,7 +315,7 @@ const checkSupport = async () => {

## Contributing

Issues and pull requests are welcome. If youre planning a larger change, open an issue first so we can discuss direction.
Issues and pull requests are welcome. If you're planning a larger change, open an issue first so we can discuss direction.

## License

Expand Down
32 changes: 19 additions & 13 deletions docs/API.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# API reference (chartgpu-react)

This package is a **React wrapper** around the `chartgpu` core library. Most runtime behavior lives in `chartgpu`; this repo primarily provides:
This package is a **React wrapper** around the `@chartgpu/chartgpu` core library. Most runtime behavior lives in `@chartgpu/chartgpu`; this repo primarily provides:

- A React component (`ChartGPU`) with lifecycle + resize management
- A small imperative ref API (`ChartGPUHandle`)
Expand All @@ -19,20 +19,20 @@ For an LLM-oriented navigation entrypoint, see [`docs/api/llm-context.md`](./api
### Hooks

- **`useChartGPU(containerRef, options)`** — create/manage an instance imperatively
- **`useConnectCharts([chartA, chartB, ...])`** — keep crosshair/interaction-x in sync
- **`useConnectCharts([chartA, chartB, ...], syncOptions?)`** — keep crosshair/interaction-x in sync (optionally sync zoom)

See [`docs/api/hooks.md`](./api/hooks.md).

### Helper re-exports (from peer dependency `chartgpu`)
### Helper re-exports (from peer dependency `@chartgpu/chartgpu`)

`chartgpu-react` exposes these helpers so you can often import everything from one package:

- `createChart`
- `connectCharts`
- `createAnnotationAuthoring`

- `createChart` / `connectCharts` are re-exported directly from `chartgpu`.
- `createAnnotationAuthoring` is a thin wrapper around `chartgpu`’s helper that includes a small fix for `chartgpu@0.2.3`: the upstream authoring context menu hit-testing does not recognize `type: "text"` annotations, so **Edit** may not appear for text notes.
- `createChart` / `connectCharts` are re-exported directly from `@chartgpu/chartgpu`.
- `createAnnotationAuthoring` is a thin wrapper around `@chartgpu/chartgpu`'s helper that patches text-annotation context-menu hit-testing (broken in `chartgpu@0.2.3`). Upstream v0.2.5 fixes this natively; the wrapper is kept for API stability — the patch is harmless when running against v0.2.5+.

```ts
import { connectCharts, createAnnotationAuthoring } from 'chartgpu-react';
Expand All @@ -56,13 +56,13 @@ From `src/types.ts`:

- `ChartGPUProps` — props for the `ChartGPU` component
- `ChartGPUHandle` — imperative ref API
- `ChartInstance` — alias for `chartgpu`’s `ChartGPUInstance`
- `ChartInstance` — alias for `@chartgpu/chartgpu`'s `ChartGPUInstance`
- `ClickParams`, `MouseOverParams` — aliases for event payloads
- `ZoomRange` — derived from `ChartGPUInstance['getZoomRange']` (non-null range)

### Re-exported core types (from peer dependency `chartgpu`)
### Re-exported core types (from peer dependency `@chartgpu/chartgpu`)

From `src/index.ts`, this package re-exports a curated set of `chartgpu` types so consumers can do:
From `src/index.ts`, this package re-exports a curated set of `@chartgpu/chartgpu` types so consumers can do:

```ts
import type { ChartGPUOptions, ChartGPUInstance, DataPoint } from 'chartgpu-react';
Expand All @@ -71,11 +71,18 @@ import type { ChartGPUOptions, ChartGPUInstance, DataPoint } from 'chartgpu-reac
Currently re-exported:

- **Core**: `ChartGPUInstance`, `ChartGPUOptions`
- **Events**: `ChartGPUEventPayload`, `ChartGPUCrosshairMovePayload`
- **Events**: `ChartGPUEventPayload`, `ChartGPUCrosshairMovePayload`, `ChartGPUZoomRangeChangePayload`
- **Hit testing**: `ChartGPUHitTestResult`, `ChartGPUHitTestMatch`
- **Chart sync**: `ChartSyncOptions`
- **Annotation authoring**: `AnnotationAuthoringInstance`, `AnnotationAuthoringOptions`, `AnnotationConfig`
- **Series config**: `AreaSeriesConfig`, `LineSeriesConfig`, `BarSeriesConfig`, `PieSeriesConfig`, `ScatterSeriesConfig`, `CandlestickSeriesConfig`, `SeriesConfig`
- **Style config**: `LineStyleConfig`, `AreaStyleConfig`
- **Data**: `DataPoint`, `OHLCDataPoint`
- **Interaction/zoom**: `DataZoomConfig`
- **Legend**: `LegendConfig`, `LegendPosition`
- **Animation**: `AnimationConfig`
- **Tooltip**: `TooltipConfig`, `TooltipParams`
- **Performance**: `PerformanceMetrics`
- **Themes**: `ThemeConfig`, `ThemeName`
- **Layout**: `AxisConfig`, `GridConfig`

Expand Down Expand Up @@ -109,10 +116,10 @@ See [`useChartGPU` docs](./api/hooks.md#usechartgpu).

### 4) Connecting charts (`useConnectCharts` / `connectCharts`)

To sync crosshair / interaction-x across multiple charts, use:
To sync crosshair / interaction-x (and optionally zoom) across multiple charts, use:

- `useConnectCharts([chartA, chartB, ...])` (React-friendly)
- or `connectCharts([chartA, chartB, ...])` (manual)
- `useConnectCharts([chartA, chartB, ...], syncOptions?)` (React-friendly)
- or `connectCharts([chartA, chartB, ...], syncOptions?)` (manual)

See [Chart sync recipe](./recipes/chart-sync.md).

Expand All @@ -126,4 +133,3 @@ Step-by-step guides for common use cases:
- [Streaming](./recipes/streaming.md) — realtime data updates with `appendData`
- [dataZoom basics](./recipes/datazoom-basics.md) — zoom and pan with `dataZoom` + `onZoomChange`
- [Scatter density](./recipes/scatter-density.md) — density heatmaps for large scatter datasets

11 changes: 7 additions & 4 deletions docs/api/chartgpu-component.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The props type is `ChartGPUProps` (defined in `src/types.ts`).
| `onMouseOver` | `(payload: ChartGPUEventPayload) => void` | | Wires to `chart.on('mouseover', ...)`. |
| `onMouseOut` | `(payload: ChartGPUEventPayload) => void` | | Wires to `chart.on('mouseout', ...)`. |
| `onCrosshairMove` | `(payload: ChartGPUCrosshairMovePayload) => void` | | Wires to `chart.on('crosshairMove', ...)`. |
| `onZoomChange` | `(range: ZoomRange) => void` | | Fires when `chart.getZoomRange()` changes (polled every 100ms). |
| `onZoomChange` | `(range: ZoomRange) => void` | | Fires on `zoomRangeChange` event. Also emits the current range once on subscribe (initial hydration). |

## Imperative ref (`ChartGPUHandle`)

Expand All @@ -41,6 +41,10 @@ The props type is `ChartGPUProps` (defined in `src/types.ts`).
- `getContainer()`
- `appendData(seriesIndex, newPoints)`
- `setOption(options)`
- `setZoomRange(start, end)`
- `setInteractionX(x, source?)`
- `getInteractionX()`
- `hitTest(e)` — note: React synthetic events require `e.nativeEvent`

See [`ChartGPUHandle`](./chartgpu-handle.md).

Expand Down Expand Up @@ -80,10 +84,9 @@ Resize calls are debounced (100ms).

### Zoom change events

If you provide `onZoomChange`, the component polls `chart.getZoomRange()` every 100ms and fires the callback when:
If you provide `onZoomChange`, the component subscribes to the upstream `zoomRangeChange` event and fires the callback whenever the zoom range changes.

- the zoom range transitions from `null` → non-null, or
- `start`/`end` values change.
On subscribe, the component also reads the current zoom range via `getZoomRange()` and fires `onZoomChange` once if the range is non-null. This ensures consumers can hydrate UI state without waiting for user interaction.

If zoom is disabled (`null`), no callback is fired.

Expand Down
50 changes: 48 additions & 2 deletions docs/api/chartgpu-handle.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
- accessing the container element
- streaming/append updates (`appendData`)
- replacing options (`setOption`)
- programmatic zoom control (`setZoomRange`)
- programmatic crosshair/tooltip (`setInteractionX`, `getInteractionX`)
- hit testing (`hitTest`)

Related:

- [`ChartGPU` component](./chartgpu-component.md)
- [Streaming recipe](../recipes/streaming.md)
- [Annotation authoring recipe](../recipes/annotation-authoring.md)
- [dataZoom basics](../recipes/datazoom-basics.md)
- LLM entrypoint: [`llm-context.md`](./llm-context.md)

## Import
Expand All @@ -25,7 +29,7 @@ import type { ChartGPUHandle } from 'chartgpu-react';

### `getChart(): ChartGPUInstance | null`

Returns the underlying `chartgpu` instance once initialized, otherwise `null`.
Returns the underlying `@chartgpu/chartgpu` instance once initialized, otherwise `null`.

Notes:

Expand Down Expand Up @@ -53,6 +57,49 @@ Replaces the chart options, delegating to `ChartGPUInstance.setOption(options)`.

Important: `setOption` is treated as a **full replacement**, not a partial merge.

### `setZoomRange(start: number, end: number): void`

Programmatically sets the zoom range in percent-space.

- **`start`**: start of zoom range (0–100).
- **`end`**: end of zoom range (0–100).

No-op when zoom is disabled on the chart (i.e. no `dataZoom` configured).

### `setInteractionX(x: number | null, source?: unknown): void`

Programmatically drives the crosshair / tooltip to a domain-space x value. Pass `null` to clear the crosshair.

- **`x`**: domain-space x value, or `null` to clear.
- **`source`** (optional): source identifier, useful for sync disambiguation (e.g. avoiding echo loops with `connectCharts`).

### `getInteractionX(): number | null`

Reads the current crosshair / interaction x in domain units. Returns `null` when the crosshair is inactive.

### `hitTest(e: PointerEvent | MouseEvent): ChartGPUHitTestResult`

Performs hit-testing on a pointer or mouse event. Returns coordinates and any matched chart element.

- **`e`**: a native `PointerEvent` or `MouseEvent` from the chart container.
- **Returns**: `ChartGPUHitTestResult` with pixel coordinates, domain-space coordinates, and an optional match object.
- If the chart is not initialized or disposed, returns a sentinel with `isInGrid: false` and `NaN` coordinates.

**React ergonomics note**: React synthetic events (`React.PointerEvent`, `React.MouseEvent`) are not the same as native DOM events. Pass `e.nativeEvent` instead:

```tsx
<div onPointerDown={(e) => {
const result = chartRef.current?.hitTest(e.nativeEvent);
// ...
}} />
```

Import the result type if needed:

```ts
import type { ChartGPUHitTestResult } from 'chartgpu-react';
```

## Example: streaming with `appendData`

```tsx
Expand Down Expand Up @@ -98,4 +145,3 @@ export function StreamingCandles() {
return <ChartGPU ref={ref} options={options} style={{ height: 420 }} theme="dark" />;
}
```

Loading