Live examples:
app/demo/reference-lines-and-bands.tsx
(lines, bands, badges) and
app/demo/working-orders.tsx
(draggable orders, custom tags, grouping) in the example app.Reference lines, bands, and off-axis badges
referenceLines accepts an array of ReferenceLines in four mutually-exclusive forms:
- Horizontal line at a
value - Time-varying line following a
series - Horizontal band between
valueFromandvalueTo - Vertical time band between
fromandto(unix seconds)
intervals), stroke opacity, and excluding their value from the axis
range (excludeFromRange). Bands can use separate fillColor / fillOpacity and
color / strokeOpacity paints. See the types reference for every field.
When the array can reorder (for example, a dynamic order book), give each line a unique id
so its rendered identity follows that line instead of its array position.
Time-varying reference line (series)
Use the series form for a historical benchmark that changes over time: average cost after
multiple fills, VWAP, a moving target, or a peg. It is especially useful over candlesticks, where
threshold split coloring does not apply.
extendToStart: false to begin the path at its first point, or extendToNow: false to stop the
path and its label at the final point. A series contributes its minimum and maximum to automatic
Y-range fitting unless excludeFromRange is set.
The series form supports the shared line presentation fields: color, strokeOpacity,
strokeWidth, intervals, label, labelColor, labelPosition, showValue, and
excludeFromRange.
Working-order fields (badge, draggable, drag callbacks) and custom tag renderers remain for
single-value Form-A lines.
Full-width lines (fullWidth)
By default a line/band stops at the plot’s right edge. Set fullWidth: true to span it
edge to edge through the Y-axis gutter, so it connects visually to its value on the axis
(like a price tag). Only the line/band extends — any label or badge stays anchored inside
the plot. For a Form-A line with a badge, the full-width line replaces the dashed connector.
When yAxis.labelRightMargin is set, a plain non-fullWidth line stops at the same X as the grid,
honoring yAxis.gridEndGap. Badge/label anchors, fullWidth lines, and value/time bands are
unchanged.
Pill badges (working orders, alerts, targets)
For a Form-A line,badge renders the value as a pill pinned to a plot edge with a dashed
connector to the opposite side — instead of a plain gutter label that collides with the Y-axis
ticks. When the value scrolls off-screen it auto-pins to the nearest edge with a directional
chevron, so a working order or alert never disappears. It pairs naturally with the
order-ticket flow.
badge: true uses the defaults (left-pinned, the line’s label/value); pass a
ReferenceLineBadgeConfig for position ("left" /
"center" / "right" — "center" floats the pill at the value with no connector), an icon,
text: false (icon-only), and the full style/shape config — background, borderColor /
borderWidth, radius, textColor, fontSize / fontFamily / fontWeight, and
offsetX / offsetY (the same knobs as the value badge).
It supersedes the legacy offAxisBadge (still supported), which only appears once the value is
off-screen and takes no icon.
Separate price pills (valueBadge)
Use a left name badge and a separate price pill for take-profit, stop-loss, and
working-order rows. Both belong to one reference line; its price text and
position follow the same UI-thread drag value, without synchronizing two lines
through onChange.
valueBadge: trueputs the price pill inside the plot’s right edge.{ position: "axis" }uses the right price column, aligned with the live badge’s body. A floating axis right-aligns it at the canvas edge. It still works when the live badge is hidden or positioned left; automatic layout reserves a gutter for axis pills. Explicitinsets.rightcontinues to take precedence.- The text always uses the chart’s
formatValue. Style it withReferenceLineValueBadgeConfig, which extendsBadgeStyleConfig: background, border, radius, text color, font, and offsets. It uses the chart font unless overridden; it does not inherit the name badge’s overrides. - The connector runs between the pills. Right/center name badges move left only
when necessary to make room for the price pill.
fullWidth: truekeeps the existing edge-to-edge stroke instead of a connector. ExistingshowValuebehavior is unchanged, including when both pills are enabled. - A price pill also works without
badge. It replaces the implicit plain price label; an explicitlabelremains and defaults to the left side. - Both pills pin together when the value is off-screen, with a directional chevron on the name pill (or the price pill when there is no name badge). Grouping counts each reference line once and hides both pills when collapsed; the count handle does not show an aggregate price.
- In
LiveChart, either pill can be tapped withonReferenceLinePressor used to begin a drag. An explicitgrabRangestill restricts where dragging starts.renderReferenceLinereplaces both built-in pills;renderOffAxisReferenceLinereplaces both only when off-screen. Custom tags retain their grouping exclusion.
LiveChartSeries
can display the pills too, with its existing reference-line interaction limits.
Try Working orders in the example app: switch Price pill between Off,
In plot, and On axis, drag either order pill, toggle Floating axis or
Live badge, and move grouped levels above/below the chart. Custom tags
still demonstrates replacing the complete built-in tag.
Draggable lines (draggable)
Make a Form-A line draggable along the Y-axis — grab it near its value and drag to set a new price
(a working order, alert level, or target). The line tracks the finger on the UI thread; per-line
callbacks report the value to JS:
onCommit with writing
the value back into value to make it controlled. onDragIn / onDragOut are edge-triggered and
also fire when the axis rescales under a fixed value (e.g. an order scrolling off the top), so
they’re a clean “is my order still on screen” signal even without dragging.
The line stays under the finger for the whole drag, also when the price range changes under a
still finger (live data, a rangeAnimation
easing) or the plot is resized. onCommit gets the value under the finger; onChange reports the
finger’s moves, and fires once more on release if the line ended elsewhere (its first call, if the
finger never changed the value). Near the top or bottom edge, a line that counts in the range
widens it only as the finger moves, so holding it there doesn’t keep pushing the range out;
dropped there, it widens the range once and settles a little inside the edge (eased with
rangeAnimation={{ animateExpansion: true }}). With snap, a line at an edge is drawn and
dropped on the nearest increment inside the visible range when one fits. Moving past the edge
rounds the finger’s value outward so a coarse increment can still widen the range.
Give draggable lines a stable id: if lines added
or removed mid-drag move the dragged line to another index, the drag then lets go instead of
moving the line that took its place. The replacement immediately gets its own value back,
including under a resting finger; bringing the original line back does not resume that touch.
LiveChart reuses its gesture configurations when controlled reference levels or
JS callbacks change. An active drag reads updated line settings, and callback
dispatch uses the latest committed props. Pass a new referenceLines array when
levels change; keep arrays and custom renderers stable when their contents have
not changed to avoid repeating reference-tag probes. Changes to gesture
recognition settings or chart layout still update the affected configuration.
Recreating a config object with the same numeric plot insets does not rebuild
the scrub gesture.
Dragging coexists with scrubbing: a press that lands within reach of a
draggable line grabs the line and any drag moves it (in any direction), while a press anywhere else
scrubs as usual. So you can keep scrub enabled alongside draggable orders — only the thin grab
band around each draggable line is reserved for the drag.
Bounds that follow a live value
A take profit or stop loss usually can’t be dragged past the current price. Passing that as a plain[min, max] means a new referenceLines array, and a chart re-render, every time the price
moves. Pass a SharedValue<[number, number]> instead and update it on the UI thread: the drag clamp
and onDragIn / onDragOut read it there, and the lines array stays the same.
onCommit
gets the clamped value.
Grab only at the handle (grabRange)
That grab band runs the whole width of the line. On a chart that also pans (timeScroll), or one
inside a scrolling page, a gesture that merely starts near a line can catch it. Give the line a
grabRange — an x-span in the canvas’s own px, ends included — and only a press inside it grabs the
line; anywhere else the touch is the chart’s (pan, scrub, page scroll), as if the line were not
there. Typically the span is a custom tag measured with
onLayout: a left-pinned tag starts 2 px inside the plot’s left edge, which is 14 px from the
canvas edge with the default insets (insets.left + 2 otherwise):
grabRange is a JS value, so leave a little slack for a tag whose width changes on the UI thread
(an off-axis chevron, a live price).
Give the order a stable id so changing its price or measured grab span keeps the custom tag
mounted.
Try Working orders in the example app: Tag only measures both custom tags and limits
grabbing to their spans; Whole line omits grabRange to compare the original behavior.
With Time scroll off, scrub through an order line away from its tag: the order price should
stay unchanged, and the Interactions log should show a scrub. Turn Time scroll on and swipe
horizontally from the same spot to browse history. Drag either tag vertically, then move your
finger away from it before releasing: the order should keep following and commit once.
Repeat with Candles on. Pause feed holds the chart steady, and Reset orders restores
the starting prices and clears the log. Selecting Tag only enables custom tags; disabling
Custom tags switches back to Whole line.
Custom tags (renderReferenceLine)
On both LiveChart and LiveChartSeries, replace a line’s built-in pill / gutter label with any
React Native view — the same model as renderMarker / renderTooltip. The chart floats your
element over the canvas and pins it to the
line’s value (vertically centered, horizontally at the badge / label position), tracking the
rescaling axis and any drag without JS re-renders. Called per Form-A line; return null to keep
that line’s built-in tag (works with badge: false too). Bind the ctx.value / ctx.valueStr
SharedValues to animated text for a live readout:
ctx (ReferenceLineRenderProps) also carries y, inRange,
edge ("above" / "in" / "below", for a directional chevron), and dragging as SharedValues.
For a left- or right-pinned badge, the built-in dashed connector stays in place
behind the native tag and starts after the tag’s measured edge; a center badge has
no connector by design.
For example, a multi-series market chart can use the app’s native icon and localized target copy
while leaving any other line on the built-in renderer:
Off-axis-only custom tags (renderOffAxisReferenceLine)
Use renderOffAxisReferenceLine when the regular label should remain in range but an
edge-pinned target needs custom chrome. The chart switches the two tags on the UI thread:
the built-in tag remains in range, and the custom element appears only above or below the
plot. Give the line a left- or right-pinned badge (or legacy offAxisBadge) to retain
the native dashed connector; it begins after the custom tag’s measured edge.
OffAxisTargetTag stays mounted so it can read the live edge SharedValue in an animated
style and flip its caret between "above" and "below". Return null from the callback
only to opt a static line / index out; do not branch on edge.get() while rendering the
callback itself. renderReferenceLine takes precedence if both callbacks handle one line.
Grouping near-value lines (referenceLineGrouping)
When a stack of orders or alerts sits near the same price, their tags pile up illegibly. Pass
referenceLineGrouping to collapse Form-A lines whose handles fall within radius px of each other
into a single count handle:
renderReferenceLine
or renderOffAxisReferenceLine are excluded from grouping (their custom tag draws itself), so
the count reflects only collapsed built-in tags. The count pill takes the same style/shape config as a per-line badge
(ReferenceLineBadgeConfig) via badge, plus a format fn
for the count label.
Badged lines far above the visible range group at the top edge, just as lines below it group
at the bottom. Off-axis lines without badge, valueBadge, or offAxisBadge are hidden, so they do not
increase a visible group’s count.
Stacking tags
Keep every order or alert readable with the opt-in stack mode:radius is the
minimum center-to-center spacing; measured heights plus a 2 px gap take precedence.
Count-pill badge and format settings apply only to mode: "collapse".
The visible live value badge reserves its actual drawn rectangle, including its
position and offsets. Set avoidValueBadge: false to disable this reservation.
A hidden or disabled badge reserves no space. If the plot cannot fit every tag,
spacing and badge avoidance are best effort; all tags remain present and their
price values stay unchanged. Increase the chart height or use collapse for very
dense sets. Tags taller than the plot may extend beyond it.
Press and drag targets follow the moved tags. Starting a drag compensates for
the tag’s initial displacement so the price does not jump to the tag’s new Y.
grabRange, snapping, bounds, and drag callbacks still apply. A custom tag’s
own Pressable continues handling its taps.
Custom renderers receive ctx.tagOffsetY, a SharedValue<number> for the extra
vertical displacement. The chart wrapper already applies this offset: do not
apply it again. ctx.y keeps the original projected/pinned line position; a
custom connector can use ctx.y + ctx.tagOffsetY. The offset is zero when stacking
is disabled and on LiveChartSeries, which does not expose grouping.
In Working orders, compare Overlap, Collapse, and Stack under
Nearby tags. Enable Dense alerts for closely spaced prices and varied
font sizes, Custom tags for draggable native views, and Avoid live badge
to compare live-price clearance. Above chart / Below chart exercise edge
pinning when Dense alerts is off. Plain alert stays hidden off-axis without
adding a phantom tag. Repeat with Candles, Floating axis, and Price pill
placement controls.
Need an overlay the built-in lines and badges can’t express — your own draggable order book or
liquidation chrome? See Overlay bridge.