Skip to main content
A reference line marks a constant value — a sales target, a capacity ceiling, a zero baseline — that the rest of the chart is read against. It’s a geom like any other: geom.rule(), layered into the spec with its own value and style.

A horizontal goal line

Bind the rule to a constant y with aes: { y: { value } }. The { value } form is a constant mapping — it pins the aesthetic to a literal instead of a column:
The rule value participates in the y-scale’s auto-domain — set a target above the tallest bar and the axis grows to fit it, so the line is never clipped off the top.

The label

A rule’s params carry its label and nothing else:
string
Optional inline text drawn alongside the line. Omit to draw a bare line.
'start' | 'end'
default:"start"
Anchors the label at the start or end of the line.
Under coord.flip() a rule runs vertically, where 'end' puts the label at the top of the line rather than at its foot. ToggleGoalLineCommand and SetStatLineCommand pick that way round for you when they build a line under a flipped coord.

Painting the line

Stroke and label typography come from the stylesheet, the same as every other mark. style.geom.rule() takes color, strokeWidth and lineType; style.geom.rule.label() takes fontFamily, fontSize, fontWeight and lineHeight. Both take a { layer } option, so a single rule can be painted without touching the others:
The built-in stylesheet paints every rule dashed at 1px in the ruleColor token, so a bare geom.rule() draws a dashed line. Redefine ruleColor through styles({ tokens: { ruleColor } }) to recolour every rule, goal line and average line at once.

Vertical lines

Mark a constant along the x-axis with aes: { x: { value } } instead:
A vertical rule requires a numeric x-axis — it marks a constant x-value, which only has meaning when x is continuous. Categorical and date x-axes support horizontal rules only. A rule reads a scalar from exactly one axis, so supplying both x and y is an INVALID_RULE_MAPPING error, as is supplying neither.

Multiple rules

Rules compose like any other layer, and paint in spec order — a rule added after geom.bar() draws in front of the bars. Stack as many as you need:
Name the rules you want painted apart — an id on the layer is what a { layer }-scoped entry addresses.

Flipped and secondary axes

A rule tracks the data space, not the screen. Under coord.flip() a { y: { value } } rule still marks a constant y — the renderer paints it as a vertical line because the y-axis now runs horizontally. To pin a rule to a secondary axis, add yScaleType: 'secondary':

An average line

A rule’s value can also be computed. Give it stat.mean() instead of a constant and the line lands at the layer’s average:

Editing a rule at runtime

Four commands cover reference lines in an editing UI: