Skip to main content
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 valueFrom and valueTo
  • Vertical time band between from and to (unix seconds)
Lines support labels, dashes (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.
Points use unix-second timestamps and must be sorted oldest to newest. The first value extends to the window’s left edge, and the last value extends flat to the live edge. Set 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: true puts 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. Explicit insets.right continues to take precedence.
  • The text always uses the chart’s formatValue. Style it with ReferenceLineValueBadgeConfig, which extends BadgeStyleConfig: 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: true keeps the existing edge-to-edge stroke instead of a connector. Existing showValue behavior is unchanged, including when both pills are enabled.
  • A price pill also works without badge. It replaces the implicit plain price label; an explicit label remains 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 with onReferenceLinePress or used to begin a drag. An explicit grabRange still restricts where dragging starts. renderReferenceLine replaces both built-in pills; renderOffAxisReferenceLine replaces both only when off-screen. Custom tags retain their grouping exclusion.
This option applies only to horizontal value lines (Form A). 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:
The line is uncontrolled by default (it stays where you drop it); pair 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.
A bound that moves under a resting finger re-clamps the line where it is drawn, and 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):
The Y reach is unchanged: a press still has to land within the grab band around the line’s value. Off-axis lines use the nearest plot edge for that grab band. Ownership is decided at touch-down. A scrub that starts outside the range can move into it; a line grabbed inside the range keeps the touch even when the finger leaves it. 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:
The custom target owns only its tag: its dashed connector still draws from the native tag’s measured edge to the plot boundary, while the midpoint keeps its built-in badge. The React Native tag is layered above the left-edge fade and scrub overlay.

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:
The clustered lines’ individual tags are suppressed and replaced by one “count” pill at the cluster’s center; the lines themselves still draw. Lines you render with 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:
Stacking moves tags vertically on the UI thread while the price strokes remain at their real values. Name and value pills are measured separately; horizontally separate columns (left, right, and price axis) pack independently. Different font sizes, badge offsets, plain in-range labels, and measured custom React Native tags all participate. Off-axis handles stay within the plot. 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.