@tanstack/charts
Version:
A chart grammar for TypeScript and JavaScript. Marks consume your data directly, channels describe visual encodings, and the engine compiles them into a renderer-neutral keyed scene. TanStack's compact scales cover common numeric and categorical mappings.
154 lines (123 loc) • 6.66 kB
Markdown
---
title: Box Marks
description: Reference for boxRows, boxY, and boxX Tukey summaries, quartiles, whiskers, outliers, lineage, styling, and interaction.
---
`boxY` summarizes raw observations into vertical boxplots. `boxX` transposes
the same statistical and interaction semantics into horizontal boxplots.
`boxRows` exposes their eager semantic preparation for reuse outside a mark.
```ts
import { boxY } from '@tanstack/charts/box'
boxY(rows, {
x: 'group',
y: 'value',
key: 'id',
fill: '#bfdbfe',
stroke: '#2563eb',
})
```
The transform and both marks are also exported from `@tanstack/charts` and
`@tanstack/charts/universal`.
## Signatures
```ts
function boxY<TDatum>(
source: Iterable<TDatum>,
options: BoxYOptions<TDatum>,
): ChartMark<BoxDatum<TDatum, InferredX>, InferredX, number>
function boxX<TDatum>(
source: Iterable<TDatum>,
options: BoxXOptions<TDatum>,
): ChartMark<BoxDatum<TDatum, InferredY>, number, InferredY>
function boxRows<TDatum>(
source: Iterable<TDatum>,
options: BoxRowsOptions<TDatum>,
): BoxDatum<TDatum, InferredCategory>[]
```
`boxY` requires a categorical `x` channel and numeric `y` channel. `boxX`
requires numeric `x` and categorical `y`.
## Summary semantics
For each non-null category, the mark:
1. keeps finite numeric observations;
2. computes linearly interpolated first quartile, median, and third quartile;
3. places Tukey fences at 1.5 times the interquartile range below and above
the box;
4. uses the lowest and highest observed values inside those fences as
whiskers; and
5. emits observations strictly outside the fences as outliers.
Categories retain first-seen order. Outliers retain their global source order,
including when category rows are interleaved. A category with no finite value
is omitted. Singleton, two-value, and zero-IQR groups use the same rules rather
than a separate fallback.
The mark composes a whisker link, interquartile bar, median tick, and outlier
dots. Those native children remain renderer-neutral; the mark does not emit a
custom SVG path.
## Eager rows
Use `boxRows` when the same summary feeds multiple marks, a table, or
application logic:
```ts
import { boxRows } from '@tanstack/charts/box'
const prepared = boxRows(rows, {
category: 'group',
value: 'measurement',
})
```
`category` and `value` use the standard `TransformValue` contract. A field
name reads that field. An accessor receives `{ datum, index, data }`.
Preparation is eager, does not mutate source rows, and returns all summary rows
followed by outliers in global source order. The result contains semantic data
and lineage only; child-mark identity remains internal to `boxX` and `boxY`.
## Options
| Option | Type | Default | Meaning |
| --------------- | ---------------------------------------------------------------------- | -------------- | ------------------------------------------------------------- |
| `id` | `string` | Layer-derived | Stable parent mark ID |
| `x` | `boxY: Channel<TDatum, ChartValue?>`; `boxX: Channel<TDatum, number?>` | Required | Category for `boxY`; finite observation for `boxX` |
| `y` | `boxY: Channel<TDatum, number?>`; `boxX: Channel<TDatum, ChartValue?>` | Required | Finite observation for `boxY`; category for `boxX` |
| `key` | `Channel<TDatum, ChartKey>` | Inferred | Stable raw-observation identity, including duplicate outliers |
| `fill` | `string` | `#ccc` | Interquartile box fill |
| `fillOpacity` | `number` | SVG default | Interquartile box fill opacity |
| `stroke` | `string` | `currentColor` | Whisker, median, and outlier stroke |
| `strokeOpacity` | `number` | SVG default | Whisker, median, and outlier stroke opacity |
| `strokeWidth` | `number` | Per child | Overrides whisker, median, and outlier widths together |
| `inset` | `number` | `0` | Pixels removed from both categorical edges of box and median |
| `r` | `number` | `3` | Outlier radius in pixels |
| `motion` | `ChartMotionDefinition<BoxDatum<...>>` | None | Motion for derived summary and outlier data |
The orientation determines the exact `x` and `y` channel types; the combined
row above is shorthand. Use `BoxYOptions` or `BoxXOptions` when naming an
options object separately.
## Derived data and lineage
The chart datum is a discriminated union:
```ts
type BoxDatum<TDatum, TCategory> =
| {
kind: 'summary'
category: TCategory
q1: number
median: number
q3: number
whiskerLow: number
whiskerHigh: number
count: number
source: readonly TDatum[]
sourceIndexes: readonly number[]
}
| {
kind: 'outlier'
category: TCategory
value: number
source: readonly [TDatum]
sourceIndexes: readonly [number]
}
```
Use `datum.kind` in tooltip, motion, or selection code. Summary lineage contains
every finite contributing observation in source order. An outlier retains its
exact source row and index.
The public type surface includes `BoxDatum`, `BoxYDatum`, `BoxXDatum`,
`BoxSummaryDatum`, `BoxOutlierDatum`, `BoxRowsOptions`, `BoxYOptions`, and
`BoxXOptions`.
## Interaction
Each category contributes one summary point owned by the box body and anchored
at the median. The whisker and median tick are decorative. Each outlier dot
contributes its own point and raw-row lineage. This keeps pointer, keyboard,
tooltip, and motion behavior from receiving duplicate summary targets for the
same category.
Supply `key` when observation identity matters across updates or when duplicate
outlier values can occur in one category.