Askr
UI & Components

Channels and Transforms

Use channels and transforms with the current published Askr packages.

  • @askrjs/askr0.0.59

How to use channels and transforms

Create a typed plot factory once, give the root stable row data, and compose only the scales, marks, and interactions the chart needs.

  1. Import the published @askrjs/askr entrypoint.
  2. Keep channels and transforms configuration next to the component, route, or server composition root that owns it.
  3. Handle pending, unavailable, cancellation, and failure states, then verify the production path.
import { createPlot } from '@askrjs/charts';

const Plot = createPlot<ProjectRow>();

<Plot.Root data={rows} rowKey={(row) => row.id} label="Revenue by day">
  <Plot.Axis axis="x" />
  <Plot.Axis axis="y" />
  <Plot.Line x="createdAt" y="revenue" />
</Plot.Root>

Channels

A channel on any mark accepts a bare row-field name, an accessor `(row, index) => value`, or a channel expression — nothing else. Bare strings are always interpreted as field names, so a literal string value (a fixed color, a fixed label) has to be wrapped in `constant("...")` or it will be read as a field lookup instead. Accessors can derive values inline, as in `y={(row) => row.value / 1_000}`.

Aggregation

`bin(input, options?)` groups a numeric or `Date` channel by thresholds, interval, or an explicit domain, and pairs naturally with `count()`, `sum(input)`, or `mean(input)` on the paired channel to build histograms and grouped summaries. `group(input)` declares the grouping key for a compound aggregate. `stack(input, options?)` and `normalize(input)` turn a numeric channel into stacked or proportional values, with `offset` (`zero`, `diverging`, `expand`) and `order` controlling how series accumulate.

Window transforms

`movingWindow(input, { window, operation?, partial? })` computes a rolling `sum`, `mean`, `min`, or `max` over a fixed row count, and `movingAverage(input, { window })` is the rolling-mean shorthand of the same helper. `regression(input, options?)` fits a linear trend either against row index or an explicit x channel. All three skip missing numeric values rather than treating them as zero, which keeps trend lines honest over sparse data.

Row lineage

Mark-level `transform` runs before layout and never mutates the source rows — `filterRows(predicate)`, `sortRows({ by, direction? })`, and `partition({ id, parentId?, children?, value, padding? })` each produce a fresh, frozen row set when they change anything, or hand back the original array untouched when they don't. Because rendering is driven off this transformed output, stable row keys are what let selection, transitions, and live updates track a row across filtering, sorting, or re-partitioning.