LiveChartSeries draws several line series sharing an axis, time window, and crosshair. It
accepts all the shared theming, axis, range, layout, and text props from
LiveChart (theme, accentColor, palette, metrics, font,
canvasMode, timeWindow, smoothing, rangeAnimation, snapKey,
yRangeScale, minRange, line, yAxis, xAxis,
referenceLines, formatValue, …), plus the multi-series props below.
formatValue(value, tickStep?) receives the source-unit interval for Y-axis
ticks, letting compact labels adapt precision. Badges and tooltips omit the
second argument; one-argument formatters remain compatible.
The inherited yAxis.intervalScale chooses dynamic nice intervals in display units without
changing any series values. Pair it with a formatValue that applies the same multiplier. If the
scaled live range cannot be represented, interval selection safely falls back to source units.
Live price changes use smoothing even when the newest recorded point already
equals SeriesConfig.value. The dot and newest drawn sample ease together;
source history and recorded scrub values remain unchanged. Earlier samples keep
their recorded geometry. smoothing={1} and snapKey still settle immediately.
On Android, the shared experimental canvasMode="opaque" option selects an opaque SurfaceView and
paints the palette background inside the canvas. See
Android surface rendering for constraints and profiling guidance.
yAxis={{ side: "left" }} also places multi-series Y-axis labels in the gutter
reserved by insets.left; right-side series value labels remain unchanged.
Centered right-gutter sizing samples the largest finite absolute series value,
skipping zero and invalid placeholders. It re-measures after the first real value
and subsequent factor-of-ten moves. insets.right overrides automatic sizing;
see Axes & grid.
With yAxis.labelRightMargin, automatic padding instead measures the actual
tick column on the UI thread, reserving its width, edge margin, the largest
dot/pulse/glow outset and six points of clearance. React layout updates only
when width changes. Inline labels and axis badges may need extra space;
insets.right remains authoritative.
Presentation clock
SharedValue<number | undefined>
UI-thread presentation head in Unix seconds. The engine reads it every frame;
undefined falls back to nowOverride or wall time. Scrub time limits use the
same clock. Lines, live dots, value lines and inline labels project at
min(viewportEnd, head), keeping windowBuffer free of future price samples.
Parked viewports retain the presented value when their edge passes the head.
Scrub callbacks, selection dots and tooltip interpolation exclude future samples;
momentum bursts originate at the projected dot and hide when it is offscreen.
An already-recorded value at or before this explicit head is presented immediately,
avoiding a second layer of live-feed tip easing. Paths and range fitting exclude
samples after an explicit head. Updating the
SharedValue does not require a React render. See
Replay clock.Growing history window
number
Authoritative history start in Unix seconds. While following without a zoom
override,
window = (now - historyStartTime) / (1 - windowBuffer) keeps this
timestamp at the left edge as time advances. Use 0 <= windowBuffer < 1.
Pan freezes the edge and unzoomed width; pinch overrides remain active.
ref.current?.resetZoom() restores the growing live window. Default zoom-out
bounds include this full span, even when the retained first sample starts later.
Non-finite or future starts fall back to timeWindow. See
All history.Series opacity
SharedValue<number>
default:"1"
A UI-thread opacity multiplier for every plotted series. Set it to
0.5
while replacement history loads, then restore it to 1 when the new data
arrives. It dims strokes, value lines, and inline value labels without
affecting axes, the legend, reference lines, or scrub UI. Live dots and pulse
rings fade completely out while the multiplier is below 1, then fade back in
once it returns to 1, avoiding translucent dots over their own strokes. See
Dimming replacement data.Manual Y-range scaling
SeriesRangeAnimationConfig
Ease the shared fitted Y-range independently of series-tip and time-window
smoothing. The first measured, visible data fits immediately, including when
it arrives after an empty mount.
animateExpansion: true opts into outward easing
for subsequent updates;
expansionSmoothing and contractionSmoothing override each direction’s
per-60fps-frame speed (0..1). Omitted speeds inherit smoothing. Omission of
the config preserves instant expansion and eased contraction. smoothing: 1,
explicit snapKey changes, and active yRangeScale dragging remain immediate.
While the fitted and displayed ranges do not overlap, contraction is limited
to the animated expansion speed so the bounds cannot cross.
Custom overlays keep following the animated ChartOverlayContext.scale.
An optional transform worklet receives { min, max, from, to } after fitting
and easing. Return finite increasing { min, max } presentation bounds; invalid
results are ignored. The untransformed fit is retained separately, so expansions
never accumulate and removing the callback restores it. The callback may read
SharedValues and receives the visible Unix-second interval. Hard bounds apply
to the underlying fit before transforming. See the
overlay range example.
See Y-range animation
for a hold/release example without a return timer.boolean | YAxisConfig
default:"true"
Inherits the value-axis configuration. Opt into vertical axis dragging with
{ scaleGesture: true } or customize it with YAxisScaleGestureConfig.
Touches use the actual left/right gutter and right-anchored price column;
the optional yRangeScale is shared by all visible series. See
Y-range scaling.SharedValue<number>
default:"1"
yAxis.scaleGesture writes this SharedValue, or uses an internal multiplier
when omitted. Disabling the gesture preserves the current multiplier.
Multiplies the shared fitted Y-range around its midpoint. Drive it from a
price-gutter gesture to stretch or compress every visible series together;
hidden series remain excluded from the underlying fit. Values must be positive
and finite, and invalid values safely behave as 1. With the inherited
nonNegative prop, zoom-out stops at the zero floor. Read on the UI thread
each frame. Series strokes are clipped vertically to the price plot while dots
and value labels retain their own placement. @experimental. See
Manual Y-range scaling.Custom overlays
(ctx: ChartOverlayContext) => ReactElement | null
Render a full-bleed custom React Native overlay with the live price↔pixel /
time↔pixel bridge.
ChartOverlayContext
includes the resolved plot rectangle, so custom off-axis tags and connectors can
stop at the same axis inset as the chart. Return null or undefined for no
overlay. See Overlay bridge.Reference-line tags
HorizontalreferenceLines support valueBadge: true or
valueBadge: { position: "axis" } for a second, formatted-price pill. The same
style and placement rules
apply; this does not add single-series dragging, grouping, or press callbacks to
LiveChartSeries.
(ctx: ReferenceLineRenderProps) => ReactElement | null
Replace a Form-A reference line’s built-in Skia badge or gutter label with any
React Native element. Return
null or undefined for per-line fallback to the
built-in tag. The custom view tracks the line’s live Y position, pins to the
nearest plot edge when off-axis, and respects left, center, and right badge
positions. A left- or right-pinned badge keeps its dashed connector, beginning
after the measured custom view. The reference-line stroke remains visible. See
Reference lines and bands.ctx.tagOffsetY is always zero on LiveChartSeries; reference-tag grouping and stacking are single-series features.
(ctx: ReferenceLineRenderProps) => ReactElement | null
Replace only an off-axis Form-A tag. The standard Skia tag or gutter label stays
in range, while the custom React Native view takes over when the line is pinned
above or below the plot. A left- or right-pinned
badge (or legacy
offAxisBadge) retains its dashed connector from the custom view’s measured
edge. Return null for lines that opt out, and use ctx.edge inside the
returned component for a directional caret. See
Reference lines and bands.Series data
SharedValue<SeriesConfig[]>
required
Array of series definitions. Read on the UI thread. Each series can override
the shared
line.simplify screen-space tolerance with simplify; 0 disables
simplification for that series. See Line denoising
and SeriesConfig.Axis labels
Shared withLiveChart. Both
default off.
boolean | AxisLabelConfig
default:"false"
Edge label at the plot’s top.
true shows the built-in value label — the
chart’s current top Y-axis bound, updated each frame. Pass
AxisLabelConfig (format / color /
position) or { render } for a custom element.boolean | AxisLabelConfig
default:"false"
Edge label at the plot’s bottom (the current bottom Y-axis bound), with the
same options as
topLabel.Legend
boolean | LegendConfig
default:"true"
Toggle chips. Configure
position, compact, and style.(id: string, visible: boolean) => void
Fires when a legend chip is tapped.
Dots
boolean | MultiSeriesDotConfig
default:"true"
Per-series live dots:
radius, ring, opt-in static glow, color,
pulse, valueLine, and valueLabel. The crisp contrasting outer ring
is on by default; glow is off by default and accepts color, radius,
blur, and opacity. Pass ring: false for a flat circle, dot={false}
to hide the dots, or color to override the per-series fill. (The deprecated
dot={{ show: false }} still hides them.)Scrub
boolean | ScrubConfig
default:"true"
markers: true (or { radius: 24 }) adds timestamp-matched markers to
onScrub. Off by default; see Marker inspection.
Crosshair scrubbing on drag. As of v2 this defaults to true (it previously
defaulted to false); pass scrub={false} to disable. The existing default is
a guide/dim layer. Omitting seriesTooltip (or setting it to false) preserves
that behavior. Set scrub.seriesTooltip to opt into the Morfi-style
time-range pill, one colour/value pill and intersection dot per visible series,
collision stacking, and edge flipping. seriesTooltip.alwaysShow keeps value
pills pinned to the visible endpoints while idle without showing the guide or
time pill. The existing scrub.tooltip={false} switch remains the master
tooltip control and also suppresses the per-series pills. See
Multi-series tooltips.
crosshairFadeDistance and crosshairLineCap style the visible guide,
selection dot, and tooltip consistently with LiveChart.boolean | SelectionDotConfig
default:"false"
Dot drawn at the scrub intersection while scrubbing. Hidden by default on
LiveChartSeries (unlike LiveChart): with multiple lines the dot can only
track the leading series, so the crosshair and the opt-in tooltip’s per-series
intersection dots mark the scrub point instead. Pass true to opt in (it anchors to the leading series),
a config to set size / color / ring, or { component } for a fully
custom Skia dot. See Scrubbing.(point: ScrubPointMulti | null) => void
Worklet callback fired on the UI thread each frame while scrubbing;
null when it ends.
Includes a per-series value array. With scrub.markers enabled it also
includes point.markers (all matches, including their data, or []). The
field is omitted when disabled. See Scrubbing.
With marker matching enabled, a valid point becoming unavailable sends null.() => void
JS-thread callback fired once when the user starts scrubbing. Respects
scrub.panGestureDelay (it fires when the pan activates, not on first touch).() => void
JS-thread callback fired once when the user stops scrubbing. Fires only if a
scrub actually started — a tap that never activates emits neither callback.
boolean | TimeScrollConfig
default:"false"
With
gesture: "axisDrag", the bottom time-axis strip is reserved for scrolling
and excluded from scrub recognition, including stationary holds.
Pan back through history (same as LiveChart). Drag (or fling) to scroll; the
chart stops auto-following until you return to the live edge. Setting it back to
false while scrolled back glides the window back to the live edge (eased, not
an instant jump; never frozen at the old position). Per-series dots / value
labels track each series’ value at the visible right edge while scrolled. Pass
a TimeScrollConfig with overscroll
to pan or pinch past the oldest data and live edge into blank space
(TradingView-style; default 0 keeps the hard stops).
@experimental. See Time-scroll.boolean | ReturnToLiveConfig
default:"true"
Controls the glide back to live when
timeScroll is switched off while scrolled
back (same as LiveChart). true (default) eases over 450 ms, false snaps
instantly, { duration } sets a custom length. Sibling of timeScroll so it
survives timeScroll={false}. @experimental. See
Time-scroll → Turning it back off.boolean | ZoomConfig
default:"false"
Pinch-to-zoom the visible window (same as
LiveChart), anchored at the focal
point. Composes with timeScroll. @experimental. See
Time-scroll.ref typed as LiveChartHandle and call
ref.current?.resetZoom() to return the shared window to timeWindow (or the growing historyStartTime span) and the live edge. This is
the same imperative reset exposed by LiveChart. See
Time-scroll → Reset zoom from a button.
(range: VisibleRange) => void
Called as the visible window changes (~1 Hz) with
{ startSec, endSec, following }. @experimental.() => void
Called once when the left edge nears the earliest retained data — the cue to
page in older history. @experimental.
Degen
boolean | DegenOptions
Chart shake + spark burst off the leading series’ dot on an upward swing.
(payload: DegenShakePayload) => void
JS-thread callback when a degen shake starts.