# @visx/xychart
In contrast to other `visx` packages which are low-level, this package seeks to abstract some of the
complexity of common visualization engineering, and exposes a **high-level** x,y (cartesian
coordinate) chart API. However, it is implemented using modularized `React.context` layers for
theme, canvas dimensions, x/y/color scales, data, events, and tooltips which allows for more
expressivity and advanced use cases.
Out of the box it supports the following:
- \* many common `<*Series />` types (animated or not) such as lines, bars, etc.
- \* `` (animated or not)
- \* `` (animated or not)
- \* `` (animated or not)
- \* ``
- \* `theme`ing
The following illustrates basic usage to create an animated line chart with a bottom `Axis`, `Grid`,
and `Tooltip`:
```tsx
import {
AnimatedAxis, // any of these can be non-animated equivalents
AnimatedGrid,
AnimatedLineSeries,
XYChart,
Tooltip,
} from '@visx/xychart';
const data1 = [
{ x: '2020-01-01', y: 50 },
{ x: '2020-01-02', y: 10 },
{ x: '2020-01-03', y: 20 },
];
const data2 = [
{ x: '2020-01-01', y: 30 },
{ x: '2020-01-02', y: 40 },
{ x: '2020-01-03', y: 80 },
];
const accessors = {
xAccessor: (d) => d.x,
yAccessor: (d) => d.y,
};
const render = () => (
(
)}
/>
);
```
See sections below for more detailed guidance and advanced usage, or explore the comprehensive API
below.
## Basic usage
Installation
```
npm install --save @visx/xychart react-spring
```
Note: `react-spring` is a required `peerDependency` for importing `Animated*` components.
Series types
The following `Series` types are currently supported and we are happy to review or consider
additional Series types in the future.
| Component name | Description | Usage |
| --------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------- | --- |
| (Animated)AreaSeries | Connect data points with a ``, with a color fill to the zero baseline | `` |
| (Animated)BarSeries | Render a `` for each data point | `` |
| (Animated)BarGroup | Group multiple child `` values together | `...` |
| (Animated)BarStack | Stack multiple child `` values together | `...` | |
| (Animated)GlyphSeries | Render a `Glyph` (any shape, defaults to ``) for each data point, e.g., a scatter plot | ` ...} />` |
| (Animated)LineSeries | Connect data points with a `` | `` |
All `Series` have animated and non-animated variants to give you more control over your bundle size,
support missing (`null`) data, and can be rendered vertically or horizontally.
Theming
Default `lightTheme` and `darkTheme` themes are exported from `@visx/xychart` and the utility
`buildChartTheme` is exported to support easy creation of custom themes.
```ts
import { buildChartTheme, XYChart } from '@visx/xychart';
import { TextProps as SVGTextProps } from '@visx/text/lib/Text'; // just for types
const customTheme = buildChartTheme({
// colors
backgroundColor: string, // used by Tooltip, Annotation
colors: string[], // categorical colors, mapped to series via `dataKey`s
// labels
svgLabelBig?: SVGTextProps,
svgLabelSmall?: SVGTextProps,
htmlLabel?: HTMLTextStyles,
// lines
xAxisLineStyles?: LineStyles,
yAxisLineStyles?: LineStyles,
xTickLineStyles?: LineStyles,
yTickLineStyles?: LineStyles,
tickLength: number,
// grid
gridColor: string,
gridColorDark: string, // used for axis baseline if x/yxAxisLineStyles not set
gridStyles?: CSSProperties,
});
() =>
```
For new charts built from low-level visx primitives, use `@visx/theme` as the shared visual token
layer. `@visx/xychart` themes remain supported for `XYChart`, while `@visx/theme` is designed for
RSC-safe CSS variable scoping, shadcn/ui-style tokens, and prop-driven primitive charts.
Existing `XYChartTheme` objects can be reused with `fromXYChartTheme`:
```tsx
import { buildChartTheme } from '@visx/xychart';
import { ThemeScope, fromXYChartTheme } from '@visx/theme';
const xychartTheme = buildChartTheme({
backgroundColor: '#ffffff',
colors: ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6'],
gridColor: '#f3f4f6',
gridColorDark: '#e5e7eb',
tickLength: 4,
});
const primitiveTheme = fromXYChartTheme(xychartTheme);
;
```
Tooltips
`@visx/tooltip` `Tooltip`s are integrated into `@visx/xychart`, and should be rendered as a child of
`XYChart` (or a child where `TooltipContext` is provided).
**`Tooltip` positioning** is handled by the `Tooltip` itself, based on `TooltipContext`. `Tooltip`
is rendered inside a `Portal`, avoiding clipping by parent DOM elements with higher z-index
contexts. See the API below for a full list of `props` to support additional behavior, such as
snapping to data point positions and rendering cross-hairs.
**`Tooltip` content** is controlled by the specified `prop.renderTooltip` which has access to:
- `tooltipData.nearestDatum` – the globally closest `Datum`, **across all** `Series`'s `dataKey`s
- `tooltipData.datumByKey` – the closest `Datum` **for each** `Series`'s `dataKey`; this enables
"shared tooltips" where you can render the nearest data point for each `Series`.
- a shared `colorScale` which maps `Series`'s `dataKey`s to `theme` colors
Event handlers
The following `PointerEvent`s (handling both `MouseEvent`s and `TouchEvent`s) are currently
supported. They may be set on individual `Series` components (e.g.,
` ...} />`), or at the chart level (e.g.,
` {}} />`) in which case they are invoked once for _every_ `*Series`.
To **disable** event emitting for any `Series` set `<*Series enableEvents=false />`. The
`onFocus/onBlur` handlers enable you to make your chart events and `Tooltip`s accessible via
keyboard interaction. Note that the current implementation requires your target browser to support
the `SVG 2.0` spec for `tabIndex` on `SVG` elements.
Below, `HandlerParms` has the following type signature:
```ts
type EventHandlerParams = {
datum: Datum; // nearest Datum to event, for Series with `dataKey=key`
distanceX: number; // x distance between event and Datum, in px
distanceY;: number; // y distance between event and Datum, in px
event: React.PointerEvent | React.FocusEvent; // the event
index: number; // index of Datum in Series `data` array
key: string; // `dataKey` of Series to which `Datum` belongs
svgPoint: { x: number; y: number }; // event position in svg-coordinates
};
```
| Prop name | Signature | `XYChart` support | `*Series` support |
| --------------- | --------------------------------------------- | ----------------- | ----------------- |
| `onPointerMove` | `(params: EventHandlerParams) => void` | ✅ | ✅ |
| `onPointerOut` | `(event: React.PointerEvent) => void` | ✅ | ✅ |
| `onPointerUp` | `(params: EventHandlerParams) => void` | ✅ | ✅ |
| `onPointerDown` | `(params: EventHandlerParams) => void` | ✅ | ✅ |
| `onFocus` | `(params: EventHandlerParams) => void` | ❌ | ✅ |
| `onBlur` | `(event: React.TouchEvent) => void` | ❌ | ✅ |
Annotations
Composable `@visx/annotations` annotations are integrated into `@visx/xychart` and use its theme and
dimension context. These components allow for annotation of individual points using
`AnnotationCircleSubject`, or x- or y-thresholds using `AnnotationLineSubject`.
[CodeSandbox](https://codesandbox.io/s/annotations-8npmf?file=/Example.tsx)
```tsx
import React from 'react';
import {
Annotation,
AnnotationLabel,
AnnotationConnector,
AnnotationCircleSubject,
Grid,
LineSeries,
XYChart,
} from '@visx/xychart';
const data = [
{ x: '2020-01-01', y: 50 },
{ x: '2020-01-02', y: 10 },
{ x: '2020-01-03', y: 20 },
{ x: '2020-01-04', y: 5 },
];
const labelXOffset = -40;
const labelYOffset = -50;
const chartConfig = {
xScale: { type: 'band' },
yScale: { type: 'linear' },
height: 300,
margin: { top: 10, right: 10, bottom: 10, left: 10 },
};
export default () => (
d.x} yAccessor={d => d.y} />
{/** Text label */}
{/** Draw circle around point */}
{/** Connect label to CircleSubject */}
);
```
##### ⚠️ `ResizeObserver` dependency
Responsive `XYChart`s, `Tooltip`, and `AnnotationLabel` components rely on
[`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)s. If your
browser target needs a polyfill, you can either pollute the `window` object or inject it cleanly
using the `resizeObserverPolyfill` prop for these components. A polyfill passed to `XYChart` will be
accessible to child `Tooltip` and `AnnotationLabel` components.
Examples ✅ ❌
❌ `Error: This browser does not support ResizeObserver out of the box`
```tsx
// no polyfill, no browser support
() =>
() =>
```
✅ No errors
```tsx
// no polyfill, target browser supports ResizeObserver
() =>
() =>
// import the polyfill in the needed module, or set it on `window` object
import ResizeObserver from 'resize-observer-polyfill';
() => // 😎
// cleanly pass polyfill to component that needs it
import ResizeObserver from 'resize-observer-polyfill';
() => (
)
```
## Advanced usage
Examples
`XYChart` is implemented using modularized `React.context` layers for scales, canvas dimensions,
data, events, and tooltips which enables more advanced usage than many other chart-level
abstractions.
By default `XYChart` renders all context providers if a given context is not available, but you can
share context across multiple `XYChart`s to implement functionality such as linked tooltips, shared
themes, or shared data.
- [`ThemeProvider` + custom theme chart background example](https://codesandbox.io/s/themeprovider-sbdvz?file=/Example.tsx)
- [`DataProvider/EventEmitterProvider` example of linked tooltips / small multiples](https://codesandbox.io/s/linked-tooltips-7s0jz?file=/Example.tsx)
- [`TooltipProvider` example of programmatic + keyboard tooltip triggering](https://codesandbox.io/s/programmatic-tooltips-hh7ly?file=/Example.tsx)
DataContext
This context provides chart canvas dimensions (`width`, `height`, and `margin`), x/y/color scales,
and a data registry. The data registry includes data from all child `*Series`, and x/y/color scales
are updated accordingly accounting for canvas dimensions.
ThemeContext
This context provides an `XYChart` theme, its used by all visual elements that compose a chart, and
can be used to render custom visual elements that are on theme.
EventEmitterContext
This context provides an event publishing / subscription object which can be used via the
`useEventEmitter` hook. `Series` and `XYChart` events, including tooltip updates, are emitted and
handled with through this context.
[CodeSandbox](https://codesandbox.io/s/eventemitterprovider-w8jhl?file=/Example.tsx)
```tsx
import React, { useState } from 'react';
import { useEventEmitter, EventEmitterProvider } from '@visx/xychart';
const eventSourceId = 'optional-source-id-filter';
const EmitEvent = () => {
const emit = useEventEmitter();
return (
);
};
const SubscribeToEvent = () => {
const [clickCount, setClickCount] = useState(0);
const allowedEventSources = [eventSourceId];
useEventEmitter('pointerup', () => setClickCount(clickCount + 1), allowedEventSources);
return
Emitted {clickCount} events
;
};
export default function Example() {
return (
);
}
```
TooltipContext
This context provides access to `@visx/tooltip`s `useTooltip` state, including whether the tooltip
is visible (`tooltipOpen`), tooltlip position (`tooltipLeft`, `tooltipTop`),
`tooltipData: { nearestDatum, datumByKey }` described above, and functions to update context
(`hideTooltip`, `showTooltip`, and `updateTooltip`).