Skip to main content
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. Set markerCluster="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.
Pass the object form to tune the fan — overlap (0–1, default 0.75) and the collapse threshold:
By default the stack fans horizontally. Pass 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:
1. Reuse the members’ lookgroupBadge: "marker" draws the representative marker’s own glyph (the newest marker in the run: its imageicon/pillkind). So a run of green + buy pills collapses to a single buy pill, not a generic count.
2. A dedicated group badge — pass a 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.
Either way, add 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:
Anchoring 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 native BlurView 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.
Custom-rendered markers are floated as RN views over the canvas, so they render crisp at native resolution (and skip the marker atlas entirely — no glyph drawn behind them). Taps still fire 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 a SharedValue<TradeEvent[]>:
Need a static line that doesn’t track the data — a price alert, target, or working order? See Reference lines & bands.