Live example:
app/demo/markers-and-trades.tsx
in the example app.Trade-fill markers and a scrolling buy/sell stream
Markers
Draw glyphs into the canvas at a(time, value) position. Markers are read on the UI thread, so
pass a SharedValue<Marker[]> and update it with .set(...) / .modify(...).
onMarkerPress fires on tap — there is no hover on touch. (It replaces the
old onMarkerHover.)Stacking co-located markers
By default markers draw at their anchor and overlap. SetmarkerCluster="stacked" to fan
co-located markers apart horizontally (overlapping, left-over-right — a stacked-coins look) at
their side ("above" / "below" / "center") — e.g. buys below the line and sells above it. Once a
cluster exceeds maxBeforeGroup (default 5) it collapses into a single count-badge marker; tapping it
fires onMarkerPress with isGrouped: true and the full members list. Grouping is recomputed every
frame, so a cluster fans back out as you zoom in.
overlap (0–1, default 0.75) and the collapse threshold:
direction: "vertical" to pile co-located markers
into a column instead — each glyph keeps its time (x) and stacks along the value axis, growing
away from the line in its side direction ("above" climbs up, "below" descends, "center" climbs
up from the line). This is the “transactions piled on the candle” look; raise maxBeforeGroup so a
tall column doesn’t collapse to a count badge early.
Custom collapsed-group badge
A collapsed group draws a round count badge by default (groupBadge: "count"). Two ways to give
it your own Skia look:
The built-in count uses the measured width of each digit, so proportional fonts such as System
remain readable. Pass an object with letterSpacing to add or remove space between count digits:
groupBadge: "marker" draws the representative marker’s own glyph
(the newest marker in the run: its image → icon/pill → kind). So a run of green + buy pills
collapses to a single buy pill, not a generic count.
MarkerGroupBadge object to draw a
glyph that’s independent of the members, for when the collapse should look different from the
individual markers (e.g. tiny dots that collapse into a distinct “Buy 5” image). It takes your own
Skia image, or an icon/pill — the same options as a single marker.
showGroupCount to stamp the member count in the glyph’s top-right corner (the
“Buy 5” look). All of this is drawn in Skia and batched with every other marker (one draw call) —
unlike renderMarker, which floats a React Native view. Tapping the group still fires
onMarkerPress with isGrouped: true and the members list, regardless of the badge style.
Built-in kind glyphs are "trade" | "boost" | "graduation" | "winner" | "clawback". Override
the glyph with icon (text/emoji) or image (a Skia SkImage). Set pill to wrap the icon in a
filled circular badge in the marker color (icon rendered white) — e.g. a green + buy / red −
sell tag:
y: pass an absolute value, or omit it to pin the marker to the line. On a
single-series chart, omitting value anchors the marker to the line at time (interpolated from
data); in multi-series, set seriesId to anchor to that series’ line. So for a marker that should
always sit on the line, just give it a time and kind.
Custom marker rendering
Built-in glyphs are drawn into the Skia canvas. When you need something the canvas can’t draw — a nativeBlurView glass badge, a gradient pill, an animated component — pass renderMarker. Return
a React Native element to float it, auto-centered, at the marker’s live (time, value) position
(tracked on the UI thread); return null/undefined to keep the built-in glyph for that marker.
onMarkerPress via the marker hit-test. renderMarker receives a second ctx argument
({ index, isGrouped, groupCount, side }) so a representative can render a distinct collapsed look.
expo-blur is the consumer’s choice — renderMarker accepts any element, and the library takes no
extra dependency.
Use
renderMarker for a handful of special markers, not hundreds of high-frequency ones: each
custom marker is its own animated view + projection, whereas built-in glyphs batch into a single
draw call. Works the same on LiveChart and LiveChartSeries.Trade stream
For trade-fill markers specifically, feed aSharedValue<TradeEvent[]>:
Need a static line that doesn’t track the data — a price alert, target, or working order? See
Reference lines & bands.