import type { LiveChartPoint, SeriesConfig, ReferenceLine } from "react-native-livechart";
.d.ts and JSDoc remain the canonical reference (your editor surfaces them on hover);
the most-used shapes are reproduced here.
Data
type CanvasMode = "transparent" | "opaque";
CanvasMode controls whether the Skia canvas participates in transparent composition or owns its
background. On Android, opaque mode selects SurfaceView. See
Android surface rendering.
interface LiveChartPoint {
time: number; // unix seconds
value: number;
}
interface CandlePoint {
time: number; // bucket start, unix seconds
open: number;
high: number;
low: number;
close: number;
volume?: number; // optional traded volume; drives the volume bars (candle mode)
}
interface SeriesConfig {
id: string;
data: LiveChartPoint[];
value: number; // latest live value
color?: string;
label?: string;
visible?: boolean; // default true
style?: "solid" | "dashed";
intervals?: [number, number];
curve?: "monotone" | "linear"; // interpolation; default "monotone"
strokeWidth?: number;
glow?: boolean;
kind?: "outcome" | "derived";
valueLabel?: string;
}
Momentum
type Momentum = "up" | "down" | "flat";
interface MomentumConfig {
threshold?: number; // default 0.12
lookback?: number; // default 20
}
Annotations
type MarkerKind = "trade" | "boost" | "graduation" | "winner" | "clawback";
interface Marker {
id: string;
time: number; // unix seconds
kind: MarkerKind;
seriesId?: string; // anchor to a series line (multi-series)
value?: number; // absolute y (takes precedence over seriesId)
color?: string;
icon?: string; // text/emoji glyph override
image?: SkImage; // image glyph override
pill?: boolean; // draw the icon in a filled circular badge
size?: number; // default 16
side?: "above" | "below" | "center"; // sit off the anchor (default "center")
data?: unknown; // surfaced on onMarkerPress
}
// Fires on tap (there is no hover on touch); `null` on a miss.
interface MarkerPressEvent {
marker: Marker;
point: { x: number; y: number };
index: number; // index in the markers array
isGrouped: boolean; // true when a collapsed cluster (count badge) was tapped
members?: Marker[]; // the cluster's markers when isGrouped
}
// Second argument to renderMarker — cluster / position state.
interface MarkerRenderContext {
index: number;
isGrouped: boolean;
groupCount: number;
side: "above" | "below" | "center";
}
// Object form of the `markerCluster` prop (tunes stacked collision). Passing it
// implies mode: "stacked" unless you set mode explicitly.
interface MarkerClusterConfig {
mode?: "anchored" | "stacked";
direction?: "horizontal" | "vertical"; // fan sideways (default) or pile into a column
overlap?: number; // 0 (touching) – 1 (stacked); default 0.75
maxBeforeGroup?: number; // collapse a run past this many; default 5
groupBadge?: "count" | "marker" | MarkerGroupBadge; // collapsed look: count circle
// (default), the representative marker's own glyph, or a
// dedicated custom badge you supply (object form)
showGroupCount?: boolean; // with a non-count badge, stamp the count in the corner; default false
}
// Badge options for a collapsed cluster. Use letterSpacing by itself to tune the
// built-in count, or add image/icon for a dedicated glyph. @experimental
interface MarkerGroupBadge {
letterSpacing?: number; // extra px between count digits; default 0
image?: SkImage; // custom Skia image (precedence over icon)
icon?: string; // text/emoji glyph (when image unset)
color?: string; // icon / pill color; defaults to a palette accent
pill?: boolean; // wrap the icon in a filled circular badge
size?: number; // glyph box size in px; default 16
}
interface TradeEvent {
side: "buy" | "sell";
price: number;
size: number;
time: number; // unix seconds
symbol?: string;
}
interface ReferenceLine {
id?: string; // stable identity; supply when lines may reorder
// Form A — horizontal line:
value?: number;
// Form B — horizontal band:
valueFrom?: number;
valueTo?: number;
// Form C — vertical time band (unix seconds):
from?: number;
to?: number;
// Shared:
label?: string;
strokeWidth?: number;
intervals?: [number, number];
color?: string;
fillOpacity?: number; // default 0.16
fullWidth?: boolean; // span edge-to-edge through the Y-axis gutter (Form A/B);
// label/badge stay in the plot. default false
labelColor?: string;
labelPosition?: "left" | "center" | "right";
showValue?: boolean;
excludeFromRange?: boolean;
// Pill badge for a Form-A line (working orders, alerts, targets) — pinned to an
// edge with a dashed connector; auto-pins with a chevron when off-screen:
badge?: boolean | ReferenceLineBadgeConfig;
badgeBackground?: string; // fallback for badge.background; default theme tooltipBg
badgeBorderColor?: string; // fallback for badge.borderColor; default the line color
badgeRadius?: number; // fallback for badge.radius; default 5
// Legacy off-axis badge (prefer `badge`, which also shows in-range + supports an icon):
offAxisBadge?: boolean;
offAxisBadgeLabel?: string;
// Draggable (Form A only) — grab the line and drag it along the Y-axis:
draggable?: boolean; // default false
snap?: number; // round the dragged value to this increment
bounds?: [number, number]; // hard-clamp the draggable value
onChange?: (value: number) => void; // during drag (de-duped to value changes)
onCommit?: (value: number) => void; // on release — pair with a controlled `value`
onDragIn?: (value: number) => void; // value entered the visible range / bounds
onDragOut?: (value: number) => void; // value left the visible range / hit a bound
}
interface ReferenceLineBadgeConfig {
position?: "left" | "center" | "right"; // edge to pin to ("center" floats at the
// value, no connector); default "left"
icon?: string; // leading glyph (chart font), like Marker.icon
text?: boolean; // show the label/value; default true. false → icon-only
background?: string; // falls back to badgeBackground, then theme tooltipBg
borderColor?: string; // falls back to badgeBorderColor, then the line color
borderWidth?: number; // border stroke width; default 1
radius?: number; // corner radius; falls back to badgeRadius, then 5
textColor?: string; // label/icon color; falls back to labelColor, then theme
fontSize?: number; // badge font size; falls back to the chart font
fontFamily?: string; // badge font family; falls back to the chart font
fontWeight?: FontWeight; // badge font weight; falls back to the chart font
offsetX?: number; // nudge the whole badge from its anchor; default 0
offsetY?: number; // nudge the whole badge from its anchor; default 0
}
interface ReferenceLineRenderProps {
line: ReferenceLine; // the line being rendered
index: number; // its index in `referenceLines`
value: SharedValue<number>; // live Y-axis value (tracks dragging)
valueStr: SharedValue<string>; // value formatted with the chart's formatValue
y: SharedValue<number>; // canvas Y px of the line (-1 when not laid out)
inRange: SharedValue<boolean>; // whether the value is within the visible range
edge: SharedValue<"above" | "in" | "below">; // for a directional off-axis chevron
dragging: SharedValue<boolean>; // whether this line is currently being dragged
}
// `renderReferenceLine` replaces a tag in every state. `renderOffAxisReferenceLine`
// keeps the built-in in-range tag and shows the custom element only for edge !== "in".
interface ReferenceLineGroupingConfig {
radius?: number; // collapse lines whose value-Y are within this many px; default 18
badge?: ReferenceLineBadgeConfig; // count-pill styling (same style/shape config as a
// per-line badge: position, icon, colors, radius,
// border, textColor, font, offset)
format?: (count: number) => string; // label the count, e.g. n => `×${n}`; default String(n)
}
interface ChartSegment {
from?: number; // unix seconds; omit → chart's left edge
to?: number; // unix seconds; omit → live edge (now)
// Scrub-focus line styling (the headline interaction):
recolorLine?: boolean; // join scrub-focus dimming; default true. false → line
// never dims (divider/label still draw); behaves as a gap
mutedColor?: string; // de-emphasis color when NOT focused; default: palette.gridLabel
// (an alpha color, e.g. "rgba(154,160,166,0.4)", fades the line)
mutedColors?: string[]; // ≥2 → de-emphasis gradient; precedence over mutedColor
active?: boolean; // force-focus this segment without scrubbing; default false
// Divider + label (colors inherit the chart palette — no base color needed):
divider?: boolean; // dashed vertical line at `from`; default false
dividerColor?: string; // default: palette.refLine
label?: string; // caption — shown only with the divider; drawn in palette.refLabel
labelPosition?: "left" | "right"; // default "left"
}
Scrub payloads
interface ScrubPointCore {
time: number;
value: number;
x: number; // canvas X
y: number; // canvas Y
}
interface ScrubPoint extends ScrubPointCore {
candle?: CandlePoint; // candle mode only
}
interface ScrubPointMulti extends ScrubPointCore {
seriesValues: { id: string; label?: string; value: number }[];
}
// Payload for onScrubAction (order-ticket mode). `price` is the value at the
// reticle Y — a chosen level — not the line value at that time.
interface ScrubActionPoint {
price: number; // reticle-Y value, optionally snap-rounded
time: number; // unix seconds at the reticle X
x: number; // canvas X
y: number; // canvas Y
candle?: CandlePoint; // candle mode only — OHLC under the reticle X (context)
}
Config objects
These are the option shapes accepted by the matching props.interface LineConfig {
width?: number; // stroke width in px; default 2
color?: string; // line color override; defaults to palette accent
colors?: string[];// per-point gradient stops (left → right); wins over color
curve?: "monotone" | "linear"; // point interpolation; default "monotone"
// (smooth cubic). "linear" = straight segments / hard-edged.
join?: "round" | "miter" | "bevel"; // corner join; default "round".
// "miter" = sharp/angular peaks, "bevel" = flattened.
cap?: "round" | "butt" | "square"; // start/end cap; default "round".
// pair "butt" + join "miter" for a fully hard-edged line.
}
interface GradientConfig {
topOpacity?: number; // accent-derived top fill opacity
bottomOpacity?: number; // accent-derived bottom fill opacity
colors?: string[]; // explicit gradient stops (top → bottom), ≥ 2 entries;
// overrides topOpacity/bottomOpacity when provided
positions?: number[]; // optional stop positions (0..1, ascending), same
// length as colors; ignored if the lengths differ
}
interface AreaDotsConfig { // dot-lattice fill of the area beneath the line
spacing?: number; // lattice pitch (px) between dots, both axes; default 12
size?: number; // dot diameter (px); default 1.6
color?: string; // dot color; omit to derive a faint tint from the line color
opacity?: number; // overall field opacity (0..1); default 1
}
interface ThresholdConfig {
// The split value (Y-axis units). A SharedValue<number> for a single live
// benchmark, OR a LiveChartPoint[] for a time-varying threshold the split,
// band + marker follow point-for-point (clamps to its first/last value outside
// its range → flat-extends to the live edge). See the Threshold split guide.
// Provide value OR series (series wins).
value?: SharedValue<number> | LiveChartPoint[];
// LIVE time-varying threshold: like the LiveChartPoint[] form but a
// SharedValue — .set()/.modify() tracks on the UI thread, no re-render
// (a VWAP updated every tick). Points sorted by time, like `data`.
series?: SharedValue<LiveChartPoint[]>;
aboveColor?: string; // stroke at/above value; default palette up-green
belowColor?: string; // stroke below value; default palette down-red
fill?: boolean | { opacity?: number }; // line↔threshold P/L band; default off, opacity 0.16
includeInRange?: boolean; // fold the threshold into the Y-range fit (like reference lines); default false
extendToNow?: boolean; // series: extend flat past the last point to "now"; default true
line?: boolean | ThresholdLineConfig; // marker line at value; default off
}
interface ThresholdLineConfig {
label?: string; // label text, e.g. "Break-even"
labelPosition?: "left" | "right"; // "left" = inside plot (clear of y-axis); default "left"
color?: string; // line + label color; default palette refLine/refLabel
labelColor?: string; // label text color; falls back to color, then palette refLabel
intervals?: [number, number]; // dash pattern; default [4, 4]
strokeWidth?: number; // default 1
showValue?: boolean; // append the formatted value to the label; default false
}
interface BadgeConfig {
variant?: "default" | "minimal";
tail?: boolean;
background?: string;
position?: "right" | "left";
// Shape / style — all Skia-native, drawn in the batched canvas pass (no perf cost):
radius?: number; // corner radius (px); default capsule (pillHeight/2), clamped to that max
borderColor?: string; // pill border color; default none
borderWidth?: number; // border stroke width (px), only drawn with borderColor; default 1
textColor?: string; // label text color override; default variant/theme rule
fontSize?: number; // badge font size (px); falls back to the chart `font`
fontFamily?: string; // badge font family; falls back to the chart `font`
fontWeight?: FontWeight; // badge font weight; falls back to the chart `font`
offsetX?: number; // nudge the whole badge horizontally (px); default 0
offsetY?: number; // nudge the whole badge vertically (px); default 0
followViewEdge?: boolean; // while time-scrolled, track the price at the visible
// window's right edge (badge value/color + value line + dot);
// default false
}
interface PulseConfig {
interval?: number; duration?: number; maxRadius?: number;
opacity?: number; strokeWidth?: number;
}
interface ValueLineConfig { strokeWidth?: number; intervals?: [number, number]; color?: string }
// Shared styling for a straight line (the connector, valueLine, …). Omit `intervals` for solid.
interface LineStyleConfig { color?: string; strokeWidth?: number; intervals?: [number, number] }
interface ScrubConfig {
tooltip?: boolean; // existing master tooltip switch; false also suppresses seriesTooltip
// LiveChartSeries only: opt-in Morfi-style time + per-series value pills.
// false/omitted preserves the existing guide-only default.
seriesTooltip?: boolean | PerSeriesTooltipConfig;
dimOpacity?: number; // opacity of content right of the crosshair; default 0.3 (1 = off)
crosshairLineColor?: string; crosshairDimColor?: string; // crosshairDimColor = legacy colored mask
crosshairStrokeWidth?: number; // line width in px; default 1
crosshairOvershoot?: number; // extend past top/bottom in px; preserves a custom top-tooltip stop; negatives clamp to 0; default 0
crosshairFade?: boolean; // fade near the live edge; default true
crosshairFadeDistance?: number; // visible line/dot/tooltip fade distance in px; negatives clamp to 0; default 4
crosshairLineCap?: "butt" | "round" | "square"; // omit to preserve the existing Skia default
crosshairDash?: number[] | boolean; // dash the crosshair line; true = [4,4], or explicit [on, off, …] px
tooltipBackground?: string; tooltipColor?: string; tooltipBorderColor?: string;
tooltipBorderRadius?: number; // pill corner radius; default 5
tooltipPlacement?: "side" | "top" | "bottom" | "point"; // default "side" (offset/flip); top/bottom center over the line; point floats above the scrub dot
tooltipMargin?: number; // gap (px) to the pinned plot edge (top for side/top, bottom for bottom); default 8
tooltipShowValue?: boolean; tooltipShowTime?: boolean; // default both true; drop a row (e.g. date-only)
panGestureDelay?: number; // press-and-hold ms before scrubbing activates; default 0
clampToPlot?: boolean; // plain scrub: ignore starts beyond the plot's X bounds and clamp active drags; default false
hideOverlaysOnScrub?: boolean; // fade markers + reference lines out while scrubbing; default false
}
interface PerSeriesTooltipConfig {
alwaysShow?: boolean; // pin value pills to visible endpoints while idle; default false
bucketSeconds?: number; // omit to infer from the first visible series
formatSeriesValue?: (value: number, seriesId: string) => string; // UI-thread worklet
formatTimeRange?: (from: number, to: number) => string; // UI-thread worklet
maxLabelChars?: number; // ellipsis truncation; default 14
guideColor?: string;
guideWidth?: number; // default 1
guideDashPattern?: boolean | number[]; // default [3,3]; false = solid
timePillBackground?: string; timePillColor?: string; timePillBorderColor?: string;
timePillRadius?: number; timePillPaddingX?: number; timePillPaddingY?: number;
seriesPillBackground?: string;
seriesPillLabelColor?: string;
seriesPillValueColor?: string;
seriesPillBorderColor?: string;
seriesPillRadius?: number;
seriesPillPaddingX?: number; seriesPillPaddingY?: number;
seriesPillDotSize?: number; seriesPillDotGap?: number;
seriesPillLabelValueGap?: number;
intersectionDotSize?: number;
}
// Order-ticket mode (scrubAction). Opt-in — default off.
interface ScrubActionConfig {
icon?: string; // action-badge glyph; default "+"
background?: string; // action-badge background; default the accent / badge color
iconColor?: string; // icon + price-text color; default the badge text color
lineColor?: string; // horizontal level-line color; default palette.crosshairLine
text?: boolean; // show the price readout; default true. false → icon-only pill
timeBadge?: boolean; // date/time pill where the line meets the x-axis; default false
snap?: number; // round the reported price to this increment (e.g. 0.01, 0.5)
dismissOnTapOutside?: boolean; // empty-plot tap dismisses vs. re-places; default false
dismissOnAction?: boolean; // dismiss the reticle once the badge fires onScrubAction; default false
}
interface SelectionDotRingConfig {
color?: string; // ring color, defaults to the dot color
width?: number; // ring thickness past the dot, default 2
}
// The scrub-intersection dot (selectionDot). Default ON for LiveChart,
// OFF for LiveChartSeries (the crosshair + opt-in per-series tooltip dots mark it).
interface SelectionDotConfig {
size?: number; // dot radius, default 4
color?: string; // dot color, defaults to the line / leading-series color
ring?: boolean | SelectionDotRingConfig; // outer halo, default on
component?: ComponentType<SelectionDotProps>; // custom Skia dot; ignores size/color/ring
}
interface GridStyleConfig { color?: string; strokeWidth?: number; intervals?: number[]; opacity?: number }
interface YAxisConfig {
minGap?: number; // min px gap between grid lines; default 36
// Show a fixed number of evenly-spaced prices (top = high, bottom = low)
// instead of the dynamic nice-interval grid. Values track the live range each
// frame. `minGap` still floors the spacing; clamped to at most 15. Default 0.
count?: number;
// Place labels in a shared left-aligned column whose right edge sits this
// many px from the canvas edge. Omit for the centered-gutter placement.
labelRightMargin?: number;
// Gap between the shared label column and each grid/plain-reference-line end.
// Only applies with labelRightMargin. Default 0.
gridEndGap?: number;
// Float the price axis over a full-width plot instead of reserving a right
// gutter — the line/candles run to the edge and the labels float on top.
// Pairs with `timeScroll` (see the Time-scroll guide). Default false. @experimental
float?: boolean;
}
interface XAxisConfig { minGap?: number } // default 60
// Volume bars below the candles (candle mode only). Bars normalize to the largest
// visible volume; the candle plot shrinks by `maxHeight` to make room. @see CandlePoint.volume
interface VolumeConfig {
upColor?: string; // bars for up (close ≥ open) candles; default palette.candleUp
downColor?: string; // bars for down candles; default palette.candleDown
maxHeight?: number; // reserved band height (px) = the tallest bar; default 48
radius?: number; // bar-top corner radius (px); default 2
opacity?: number; // opacity of the whole band (0..1); default 0.6
}
// Horizontal time-scrolling (pan back through history). @experimental
interface TimeScrollConfig {
gesture?: "holdToScrub" | "axisDrag"; // activation; default "holdToScrub"
scrubHoldMs?: number; // holdToScrub: press-hold (ms) before scrub engages; default 500
hideLiveOnScrollBack?: boolean; // hide live badge + dot + value line while scrolled back (they mark the off-screen live price); default true. Ignored with badge.followViewEdge
}
// Tunes the return-to-live glide (the `returnToLive` prop). @experimental
interface ReturnToLiveConfig {
duration?: number; // glide duration (ms) back to live; default 450 (0 = instant snap)
}
// Tune/disable the built-in transition animations — the object form of `transitions`.
interface TransitionConfig {
reveal?: number; // grow-in/collapse on data appear, timeframe change, candle→line; default 600 (0 = snap)
mode?: number; // candle↔line crossfade (single-series LiveChart only); default 300 (0 = snap)
candleLerpSpeed?: number; // candle-width ease speed (0–1, like smoothing); candle mode; default 0.08 (1 = instant resize)
}
// Styling for the breathing loading shell — the object form of `loading`.
interface LoadingConfig {
color?: string; // squiggle + skeleton color; default theme gridLine
strokeWidth?: number; // squiggle stroke width; default the chart line strokeWidth
amplitude?: number; // base breathing-wave height (px); default 14
speed?: number; // breathing-wave speed multiplier; default 1 (0 freezes)
axisLabels?: boolean; // draw the skeleton Y-axis placeholders; default true (false = squiggle only)
}
// Pinch-to-zoom the visible time window (two-finger, focal-anchored). @experimental
interface ZoomConfig {
minTimeWindow?: number; // tightest window in seconds (max zoom-in); default timeWindow / 8
maxTimeWindow?: number; // widest window in seconds (max zoom-out); default the full data span
}
// Visible window reported by onVisibleRangeChange (unix seconds). @experimental
interface VisibleRange {
startSec: number; // left-edge time of the visible window
endSec: number; // right-edge time of the visible window
following: boolean; // true when at the live edge (not scrolled back / paused)
}
// Edge label (topLabel / bottomLabel). Default off — both opt-in.
interface AxisLabelConfig {
format?: (v: number) => string; // built-in value formatter; defaults to formatValue
color?: string; // text color; defaults to palette.gridLabel (muted)
// "left"/"right" pin to that edge (default "right"). "extrema" floats the label
// on the actual high (topLabel) / low (bottomLabel) data point. "extrema-edge"
// keeps the label on the top/bottom edge (x-aligned) with a dot + connector.
position?: "left" | "right" | "extrema" | "extrema-edge";
fontSize?: number; // built-in value text size (px); default 11
fontWeight?: FontWeight; // built-in value text weight; default platform default
fontFamily?: string; // built-in value text font family
dotColor?: string; // extrema modes — dot color; defaults to color
dotSize?: number; // extrema modes — dot diameter (px); default 7
dot?: boolean; // extrema modes — draw the marker dot; default true
// "extrema-edge" connector (dot → edge label). true = dashed default, false =
// none, or a LineStyleConfig. Default on (dashed) in "extrema-edge" mode.
connector?: boolean | LineStyleConfig;
render?: () => React.ReactElement | null; // custom element; overrides everything above
}
interface ChartInsets { top?: number; right?: number; bottom?: number; left?: number }
interface LeftEdgeFadeConfig { width?: number; startColor?: string; endColor?: string }
interface DotRingConfig {
color?: string; // default: theme `badgeOuterBg`
width?: number; // default 2.5
}
// Shared live-dot styling (LiveChart `dot`; LiveChartSeries `dot` extends it)
interface DotConfig {
radius?: number; // color-filled dot radius, default 3.5
ring?: boolean | DotRingConfig; // haloed outer ring, default true
show?: boolean; // @deprecated — prefer dot={false}; default true
color?: string; // dot fill, defaults to the line color
}
interface MultiSeriesDotConfig extends DotConfig {
pulse?: boolean | PulseConfig;
valueLine?: boolean | ValueLineConfig;
valueLabel?: boolean;
}
interface LegendConfig {
visible?: boolean;
compact?: boolean;
position?: "top" | "bottom";
style?: LegendStyle;
}
interface FontConfig {
fontFamily?: string; // default "Menlo"
fontSize?: number; // default 11
fontWeight?: FontWeight;
typeface?: DataSourceParam; // require(...) / path / Uint8Array
fontManager?: SkFontMgr | null;
}
AxisLabelConfig
Shape for thetopLabel / bottomLabel props. Both are opt-in (default off).
With true, the chart floats its current top (topLabel) / bottom
(bottomLabel) Y-axis bound at that edge, formatted with format (falling back
to the chart’s formatValue) in color (falling back to the muted
palette.gridLabel), aligned by position. The values track the live Y-axis
range each frame on the UI thread. Set render to float a fully custom element
at the edge instead of the built-in value. position: "extrema" instead floats
the label (a dot + value, or your render element) at the actual data point
where the high / low occurs, tracking it as the chart scrolls. See
Axis labels and
Marking the extrema points.
SelectionDotProps
Props passed to a customselectionDot.component — the dot drawn at the scrub
intersection. Every positional input is a SharedValue, so the dot animates on
the UI thread without re-renders.
interface SelectionDotProps {
x: SharedValue<number>; // scrub X in canvas px
y: SharedValue<number>; // scrub Y (line / value intersection)
active: SharedValue<boolean>; // whether scrubbing is active
opacity: SharedValue<number>; // crosshair fade opacity (0..1), already ramped
color: string; // resolved dot color (line / series accent)
size: number; // suggested dot radius in px
}
TooltipRenderProps
Context passed to a customrenderTooltip — the fully custom scrub tooltip. The element you
return is a React Native view the chart floats over
the canvas and positions on the UI thread (per scrub.tooltipPlacement). Every live field is a
SharedValue, so bind them to animated text (e.g. an Animated.createAnimatedComponent(TextInput)
driven by useAnimatedProps) and both movement and text stay on the UI thread — no JS
re-render while scrubbing. See Custom tooltip.
interface TooltipRenderProps {
value: SharedValue<number | null>; // value under the crosshair (candle mode: the close)
time: SharedValue<number>; // window time (unix seconds) under the crosshair
valueStr: SharedValue<string>; // value formatted with the chart's formatValue
timeStr: SharedValue<string>; // time formatted with the chart's formatTime
active: SharedValue<boolean>; // whether scrubbing is active
// Candle mode: the OHLC bucket under the crosshair (null in line mode / inactive).
candle: SharedValue<CandlePoint | null>;
}
ChartOverlayContext
The price↔pixel / time↔pixel bridge passed to a customrenderOverlay on LiveChart or
LiveChartSeries. It hands you a single per-frame scale SharedValue plus pure
mapping worklets.
interface ChartOverlayContext {
scale: SharedValue<ChartScale>; // live scale, recomputed each frame
priceToY: (price: number, scale: ChartScale) => number; // price → canvas Y px (-1 before layout)
yToPrice: (y: number, scale: ChartScale) => number | null; // canvas Y px → price (clamped; null before layout)
timeToX: (time: number, scale: ChartScale) => number; // unix seconds → canvas X px
xToTime: (x: number, scale: ChartScale) => number; // canvas X px → unix seconds
}
interface ChartScale {
min: number; // live Y-axis lower bound (price at the plot bottom)
max: number; // live Y-axis upper bound (price at the plot top)
window: number; // visible time window in seconds
now: number; // right-edge timestamp (unix seconds)
plot: ChartPlotRect;
}
interface ChartPlotRect {
left: number; // padding.left
top: number; // padding.top
right: number; // canvasWidth - padding.right
bottom: number; // canvasHeight - padding.bottom
width: number; // full canvas width
height: number; // full canvas height
}
usePriceY / useTimeX hooks. They project a price / time to
a SharedValue<number> that tracks the live axis for you. Render one component per
level and read the value in your useAnimatedStyle:
function PriceLevel({ ctx, price }) {
const y = usePriceY(ctx, price); // reactive — tracks the rescaling axis
const style = useAnimatedStyle(() => ({ transform: [{ translateY: y.get() }] }));
return <Animated.View style={style} />;
}
scale.get() inside your worklet — that read subscribes it to
the live scale — then feed the snapshot to the pure mappings. (Reanimated only
observes SharedValues read directly in a worklet’s closure, so the mappings take the
snapshot as an argument rather than reading it themselves.)
const style = useAnimatedStyle(() => {
const s = scale.get(); // subscribe to the live scale
const y = priceToY(142.5, s); // project a price level to its pixel
return { transform: [{ translateY: y }] };
});
DegenOptions
The most-used fields (see the source for the full tuning surface):interface DegenOptions {
scale?: number; // default 1
downMomentum?: boolean; // also trigger on down swings; default false
shake?: boolean; // default true
shakeIntensity?: number;
shakeDurationSec?: number; // default 0.45
burstParticleCount?: number; // default 20
particleOpacity?: number; // default 0.55
colors?: string | string[];
// ...plus particle slot/size/speed/jitter tuning
}
Palette
accentColor + theme derive a full LiveChartPalette. Override any subset via the palette
prop. Frequently overridden keys:
interface LiveChartPalette {
line: string;
fillTop: string; fillBottom: string;
gridLine: string; gridLabel: string;
dotUp: string; dotDown: string; dotFlat: string;
candleUp: string; candleDown: string; wickUp: string; wickDown: string;
dashLine: string; refLine: string; refLabel: string;
crosshairLine: string; crosshairDim: string;
tooltipBg: string; tooltipText: string; tooltipBorder: string;
// ...glow, badge, and font-size keys
}
Metrics
The geometry/motion analogue ofLiveChartPalette. Override any subset via the metrics prop
(per-namespace shallow merge — only the keys you set are replaced). See the
Theming guide.
interface LiveChartMetrics {
badge: BadgeMetrics;
candle: CandleMetrics;
grid: GridMetrics;
motion: MotionMetrics;
emptyState: EmptyStateMetrics;
}
interface BadgeMetrics {
padX: number; // pill horizontal padding, default 10
padY: number; // pill vertical padding, default 3
tailLength: number; // tail spike toward the dot, default 5
marginEdge: number; // gap from the canvas edge, default 4
dotGap: number; // gap between the dot and the tail tip, default 12
tailSpread: number; // tail curve control-point spread, default 2.5
}
interface CandleMetrics {
minBodyPx: number; // min body height so dojis stay visible, default 1
maxBodyPx: number; // max body width, default 40
bodyWidthRatio: number; // body width as a fraction of the slot, default 0.8
bodyRadius: number; // body corner radius (px); 0 = sharp, default 0
wickWidth: number; // wick (high–low line) stroke width (px), default 1
}
interface GridMetrics {
fadeInSpeed: number; // per-frame alpha lerp when a line/label fades in, default 0.18
fadeOutSpeed: number; // per-frame alpha lerp when it fades out, default 0.12
}
interface MotionMetrics {
badgeColorSpeed: number; // badge color transition lerp speed, default 0.08
adaptiveSpeedBoost: number; // extra catch-up over `smoothing` when the value lags, default 0.12
}
interface EmptyStateMetrics {
labelOpacity: number; // empty-state label opacity, default 0.35
gapPad: number; // half-padding around the empty text, default 20
gapFadeWidth: number; // fade width each side of the empty-text gap, default 30
}
// `metrics` prop type — every namespace and field optional:
type LiveChartMetricsOverride = {
badge?: Partial<BadgeMetrics>;
candle?: Partial<CandleMetrics>;
grid?: Partial<GridMetrics>;
motion?: Partial<MotionMetrics>;
emptyState?: Partial<EmptyStateMetrics>;
};