> ## Documentation Index
> Fetch the complete documentation index at: https://react-native-livechart.brandtnewlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Types

> Exported TypeScript types — data shapes, config objects, and the palette.

All types are exported from the package root:

```tsx theme={null}
import type { LiveChartPoint, SeriesConfig, ReferenceLine } from "react-native-livechart";
```

The source `.d.ts` and JSDoc remain the canonical reference (your editor surfaces them on hover);
the most-used shapes are reproduced here.

## Data

```ts theme={null}
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](/guides/android-surface-rendering).

```ts theme={null}
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

```ts theme={null}
type Momentum = "up" | "down" | "flat";

interface MomentumConfig {
  threshold?: number; // default 0.12
  lookback?: number;  // default 20
}
```

## Annotations

```ts theme={null}
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

```ts theme={null}
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.

```ts theme={null}
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 the `topLabel` / `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](/guides/axes-and-grid#axis-edge-labels) and
[Marking the extrema points](/guides/extrema-labels).

## SelectionDotProps

Props passed to a custom `selectionDot.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.

```ts theme={null}
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 custom `renderTooltip` — 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](/guides/scrubbing#custom-tooltip-rendertooltip).

```ts theme={null}
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 custom
[`renderOverlay`](/api-reference/livechart#renderoverlay) on `LiveChart` or
`LiveChartSeries`. It hands you a single per-frame `scale` `SharedValue` plus pure
mapping worklets.

```ts theme={null}
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
}
```

**Easiest path — the `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`:

```tsx theme={null}
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} />;
}
```

**Manual path.** Read `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.)

```tsx theme={null}
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):

```ts theme={null}
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:

```ts theme={null}
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 of `LiveChartPalette`. Override any subset via the `metrics` prop
(per-namespace shallow merge — only the keys you set are replaced). See the
[Theming guide](/guides/theming#sizing-and-motion-tokens).

```ts theme={null}
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>;
};
```
