Skip to content

Geoms

A geom is the mark you actually see on the page — a dot, a line, a bar. In code it’s a factory: geom(channels, options?) returns a Geom<T>, and you add it to a chart with .layer(...). Stack as many as you like; layers draw in the order you declare them, so the last one sits on top. Every channel you pass is an Aes<T, V> — a column name, an accessor, or a constant — and the channels object is where mappings go while options is for fixed styling.

Here’s the full vocabulary, all exported from insomni-plot:

aggregate, area, band, bar, boxplot, bullet, connectedScatter, dumbbell, ganttTrack, histogram, interval, line, lollipop, point, radar, ribbon, ridgeline, rug, rule, smooth, statRolling, text, tile, violin.

The rest of this page walks each one. They share a rhythm: a line on what it’s for, the channels it reads, and the options that style it.

Your bread-and-butter scatter and bubble marks. x and y are the only things it can’t do without; map size and you’ve got a bubble chart, map color and you’ve split it by category.

ChannelTypeRequired
x, ynumber | Dateyes
colorany (categorical or continuous)no
sizenumberno
shapecategorical → shape paletteno
alphanumberno
borderStylecategorical → solid | open | dashed | dottedno
overlayGlyphcategorical → secondary glyphno
overlayScalenumberno

Options: fill, radius, stroke, strokeWidth, shape (PointShapeKind), borderStyle, overlayGlyph, overlayScale, label, and glow ({ color?, radius?, alpha? } — a soft translucent disc behind each marker, handy for making a highlighted point pop).

import { plot, point } from "insomni-plot";
plot<Row>({ data }).layer(
point({ x: "weight", y: "mpg", color: "origin", size: "hp" }, { radius: 3 }),
);

A connected polyline. Hand it a color channel and it splits the data into one stroke per category — exactly what you want for a multi-series time series. Use order if your rows aren’t already in the sequence you want drawn.

ChannelTypeRequired
x, ynumber | Dateyes
colorcategoricalno
ordernumber | Dateno

Options: stroke, strokeWidth, curve (LineCurve), curveSamples, dashPattern, dashStyle (dashed \| dotted), nearestX, label.

import { line } from "insomni-plot";
plot<Row>({ data }).layer(
line({ x: "date", y: "price", color: "ticker" }, { curve: "monotone-x" }),
);

A line whose vertex order is driven by a third variable, with optional point heads at each datum. Great for trajectories through a two-variable space — think country-level GDP vs. life-expectancy walks over time.

ChannelTypeRequired
x, ynumber | Dateyes
ordernumber | Dateyes
colorcategoricalno
size, alphanumberno
shapecategoricalno

Options: line (LineOptions), point (PointOptions or false for a bare path).

Filled area from a baseline. A single y column draws 0 → y; an array of column keys draws a stacked area keyed by column name. Reach for position: "fill" when you care about proportions more than absolute values.

ChannelTypeRequired
xnumber | Dateyes
ynumber | Date or (keyof T)[]yes
colorcategoricalno

Options: fill, stroke, strokeWidth, position ("identity" / "stack" / "fill"), order (StackOrder), nearestX, curve (LineCurve), curveSamples, label.

import { area } from "insomni-plot";
plot<Row>({ data }).layer(area({ x: "date", y: ["us", "eu", "asia"] }, { position: "stack" }));

Categorical bars — the workhorse of comparison charts. A single y column draws one bar per category; an array of column keys draws multi-series bars. Orientation is auto-detected from scale types and can be forced with orientation.

ChannelTypeRequired
xstring | number | Dateyes
ystring | number | Date or (keyof T)[]yes
colorcategoricalno

Options: orientation, fill, stroke, strokeWidth, cornerRadius, borderStyle, position (BarPosition), order, groupPadding, showTotals ((total, datum, index) => string), showValues ((value, datum, index, key?) => string), labelColor, labelFontSize, label.

import { bar } from "insomni-plot";
plot<Row>({ data }).layer(
bar({ x: "quarter", y: ["q1", "q2", "q3", "q4"] }, { position: "dodge" }),
);

Bins one numeric variable and counts the results. Provide exactly one of x (vertical bars) or y (horizontal). A color channel splits the sample into per-group bins for side-by-side or stacked comparisons.

ChannelTypeRequired
x or ynumberone of
colorcategoricalno

Bin selection (in priority order): breaksbinwidthbinsrule (sturges / rice / scott / fd). Other options: domain, nice, closed, y measure (count / frequency / density / proportion), position, mirror, fillAlpha, fill, stroke, strokeWidth, cornerRadius, gap, groupPadding, showCounts.

import { histogram } from "insomni-plot";
plot<Row>({ data }).layer(histogram({ x: "weight" }, { rule: "fd", y: "density" }));

A regression fit with an optional confidence ribbon. One curve per color group. Pair it with point() on the same data for the classic scatter + trend combo.

ChannelTypeRequired
xnumber | Dateyes
ynumberyes
colorcategoricalno

Options: method (SmoothMethod"lm" default, "poly", "loess"), degree, span, ci (true default = 95% ribbon, false disables the ribbon, a number sets an explicit confidence level), samples, stroke, strokeWidth, ribbonFill, ribbonOpacity, label, nearestX.

import { point, smooth } from "insomni-plot";
plot<Row>({ data })
.layer(point({ x: "weight", y: "mpg" }))
.layer(smooth({ x: "weight", y: "mpg" }, { method: "loess", span: 0.6 }));

A rolling-window statistic — moving average, sum, min, max — drawn as a line. Useful for taming noisy time series without switching to a separate smoothing model.

ChannelTypeRequired
xnumber | Dateyes
ynumberyes
colorcategoricalno

Options: window (RollingWindow — required), statistic (default "mean"), axis, filter, curve, stroke, strokeWidth, dashStyle, nearestX (default true), label.

A filled band between two y-bounds at each x. Reach for this when you have a pre-computed range — confidence intervals, forecast envelopes, sensor min/max — and you want the fill without the stat layer.

ChannelTypeRequired
xnumber | Dateyes
y0, y1number | Dateyes

Options: fill, stroke (both accept a Color or a theme accent key), strokeWidth, fillAlpha, curve (LineCurve), curveSamples, label.

Error bars and range marks. Bind either (yMin, yMax) for vertical intervals (then x is required) or (xMin, xMax) for horizontal (then y is required) — not both. Pair with point() for the classic mean ± CI look.

ChannelTypeRequired
xnumber | Datewhen yMin/yMax set
ynumber | Datewhen xMin/xMax set
yMin, yMaxnumber | Datevertical pair
xMin, xMaxnumber | Datehorizontal pair
colorcategoricalno

Options: stroke (Color or accent key), strokeWidth, caps, capWidth, label.

A static reference band spanning an x- or y-range. Channels take a [start, end] tuple rather than a data column, so it’s a decoration you add once — think target zones, normal ranges, or weekend shading.

ChannelTypeRequired
x[number | Date, number | Date]one of
y[number | Date, number | Date]one of

Options: fill / stroke (Color or accent key), strokeWidth, alpha, label, labelColor.

A reference line at a constant value. x draws a vertical line; y draws a horizontal one. Reach for it when you need a zero-line, a threshold, or a goal marker without touching the data.

ChannelTypeRequired
x or ynumber | Dateone of

Options: stroke (Color or accent key — positive / negative / warn / info), strokeWidth, dashPattern, label, labelColor, labelInset, endCap ("none" (default) / "arrow" / "triangle"), endCapSize.

Marginal tick marks along the axes — the minimal marginal distribution. Stack one on top of your scatter and you get a quick read on data density without committing to a full histogram. side defaults to whichever channels are wired.

ChannelTypeRequired
xnumber | Dateno
ynumber | Dateno
colorcategoricalno

Options: side ("x" / "y" / "both"), length, strokeWidth, stroke, opacity, label.

Per-row text labels anchored to data positions. Use collisionMode to keep a dense dataset readable — "hide" culls overlapping labels, "stagger" nudges them into rows.

ChannelTypeRequired
x, ynumber | Date | stringyes
textstringyes

Options: fontSize, color, align, offsetX, offsetY, box (rounded-rect background), collisionMode ("none" / "hide" / "stagger"), collisionPadding, label.

Heatmap cells. Map a numeric column to fill and the color scale does the work; omit it and pass options.fill for a constant color. Works best on a band × band grid where every cell is the same size.

ChannelTypeRequired
x, ystring | number | Dateyes
fillany (continuous)no

Options: fill, padding / paddingX / paddingY, stroke, strokeWidth, cornerRadius, showValues, labelColor, labelFontSize, minLabelCellPx, cellWidth, cellHeight, na, label.

Box-and-whisker summaries — the five-number summary at a glance. The band axis is the category; the other is the distribution. Map color to dodge side-by-side groups within each band.

ChannelTypeRequired
xstring | number | Dateyes
ystring | number | Dateyes
colorcategoricalno

Options include orientation, width, varwidth, whisker (WhiskerRule), quantile (QuantileMethod), notched, notchWidth, points (PointsMode"auto" / "always" / "none"), pointsThreshold, pointJitter, outliers, fill, fillAlpha, stroke, strokeWidth, mean (true for a default red dot, or MeanMarkerOptions to customize), medianStroke, medianStrokeWidth, whiskerStrokeWidth, capWidth, groupPadding, showCounts, countsOffset, countsFontSize, countsColor.

KDE density per category, mirrored into a symmetrical shape. Where a boxplot compresses the distribution into five numbers, a violin shows the full shape — worth the extra ink when multimodality matters. Optionally annotate the interior with a box, quartile lines, or raw sticks.

ChannelTypeRequired
xstring | number | Dateyes
ystring | number | Dateyes
colorcategoricalno

Options include orientation, width, bandwidth (KdeBandwidth), gridSize, kernel (KdeKernel), trim, scale ("width" / "area" / "count"), inner ("none" / "box" / "quartile" / "stick"), points, pointsThreshold, pointJitter, pointRadius, jitterSeed, whisker, quantile, fill, stroke, strokeWidth, innerStroke, innerStrokeWidth, groupPadding, showCounts, countsOffset, countsFontSize, countsColor.

Stacked density ridges — the “joyplot.” Great for showing how a distribution shifts across a categorical dimension (months, cohorts, sensors) without burning a panel on each one. Each row gets its own KDE or histogram, and the ridges overlap by scale.

ChannelTypeRequired
x, ystring | numberyes (one numeric, one category)
colorcategoricalno

Options include orientation, geom ("kde" / "histogram"), overlap, scale ("width" / "area" / "count"), KDE pass-throughs (bandwidth, gridSize, kernel, trim), histogram pass-throughs (bins, binwidth, breaks, rule, measure, closed), fillMode ("solid" / "gradient"), gradient, inner ("none" / "median" / "quartile" / "mean"), innerStroke, innerStrokeWidth, innerDotRadius, baseline, baselineStroke, baselineWidth, fill, fillAlpha, stroke, strokeWidth, groupPadding, whisker, quantile, showCounts, countsAnchor, countsOffset, countsFontSize, countsColor.

import { ridgeline } from "insomni-plot";
plot<Row>({ data }).layer(ridgeline({ x: "temp", y: "month" }, { scale: 2.4, inner: "median" }));

Bins along an axis and reduces each bin to a summary statistic, rendering an inner geom for the result. Falls back to the raw geom when bins are too sparse (dissolveAt) — so it gracefully handles zoomed-out views without extra wiring.

ChannelTypeRequired
x, ynumber | Dateyes

Options: binBy, binSize ("auto" default), autoTargetPx (target px per bin when binSize: "auto", default 7), summary (scalar like "mean" / "median", or a bundle like "mean+ci" / "median+iqr"), filter, dissolveAt, geom ("point" / "line" / "bar" / "interval" / "ribbon"), fill, stroke, radius, shape (PointShapeKind), curve, ciLevel, caps, capWidth, fillAlpha, label.

import { aggregate } from "insomni-plot";
plot<Row>({ data }).layer(
aggregate(
{ x: "timestamp", y: "latency" },
{
summary: "mean+ci",
geom: "ribbon",
},
),
);

A horizontal bullet gauge: a measure bar with an optional vertical target notch. The compact format — one row per metric — makes it easy to compare actuals against goals across many categories at once. Use with a band y scale and a continuous x scale.

ChannelTypeRequired
ystring | number | Dateyes
xnumberyes
targetnumberno

Options: fill (Color or Gradient for the measure bar), target (BulletTargetOptionsstroke, strokeWidth), bar (forwarded cornerRadius, stroke, strokeWidth, label from BarOptions).

import { bullet } from "insomni-plot";
plot<Row>({ data }).layer(bullet({ y: "category", x: "value", target: "goal" }));

A connector line between a low and high x value, with endpoint circles — the go-to mark for before/after or range comparisons by category. Each datum emits two hit targets (seriesKey: "lo" / "hi") so tooltips anchor to the nearest endpoint.

ChannelTypeRequired
ystring | number | Dateyes
xLonumberyes
xHinumberyes
colorcategoricalno

Options: loColor (default cool blue), hiColor (default warm red), radius (endpoint circle radius, default 6), connector ({ stroke?, strokeWidth? }).

import { dumbbell } from "insomni-plot";
plot<Row>({ data }).layer(dumbbell({ y: "label", xLo: "before", xHi: "after" }));

Gantt rows: one filled rounded rectangle per datum spanning [xMin, xMax] on the continuous x axis, at the datum’s band row on y. Perfect for timelines, schedules, and project plans. Optionally backed by a faint full-row track rectangle to ground each row visually.

ChannelTypeRequired
ystring | number | Dateyes
xMinnumberyes
xMaxnumberyes
colorcategoricalno

Options: cornerRadius (default 4), barPadding (vertical inset fraction 0..0.5, default 0.18), fill (fallback color), track (false to hide background track, or { fill?, cornerRadius? } to customize).

import { ganttTrack } from "insomni-plot";
plot<Row>({ data }).layer(ganttTrack({ y: "task", xMin: "start", xMax: "end", color: "phase" }));

Stick-and-dot chart: a vertical (or horizontal) interval from a baseline to each value, optionally topped with a point head. A lighter-weight alternative to bars when the dots do more communicative work than the filled rectangles.

ChannelTypeRequired
xnumber | Dateyes
ynumberyes
colorcategoricalno

Options: baseline (constant or accessor, default 0), stick (IntervalOptions), point (PointOptions or false for a bare stick), orientation ("y" = vertical sticks (default), "x" = horizontal).

import { lollipop } from "insomni-plot";
plot<Row>({ data }).layer(lollipop({ x: "category", y: "value", color: "group" }));

Polar spider chart: a closed filled polygon (area) ordered around the angular axis, optionally topped with vertex points. Use with coordPolar({ gridShape: "polygon" }) and a band angle scale. Map color to overlay multiple series on the same web for direct comparison.

ChannelTypeRequired
xcategoricalyes
ynumberyes
colorcategoricalno
alphanumberno

Options: area ({ fill?, stroke?, strokeWidth? }), point (PointOptions or false to omit vertex dots).

import { radar } from "insomni-plot";
plot<Row>({ data })
.coordPolar({ gridShape: "polygon" })
.layer(...radar({ x: "axis", y: "score", color: "series" }));