### Install and Start Example App Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/README.md Instructions to install dependencies and start the example application from the repository root. ```bash npm install npm start ``` -------------------------------- ### Install Dependencies and Start Development Server Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/CONTRIBUTING.md Installs all project dependencies for the monorepo and starts the Expo development server for the example app. ```bash npm install # installs root + workspace deps npm start # expo start (dev server for the example app) npm run ios # build & run the example on iOS ``` -------------------------------- ### Development Server and App Execution Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/CLAUDE.md Start the Expo development server for the example app or run the app on iOS. ```bash npm start # expo start (dev server for the example app) npm run ios # expo run:ios ``` -------------------------------- ### Install react-native-livechart Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/installation.mdx Install the library using npm. ```bash npm install react-native-livechart ``` -------------------------------- ### Basic Order Ticket Setup Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/scrubbing.mdx Use `scrubAction` to enable the order ticket functionality. The `onScrubAction` callback will be invoked when the action badge is pressed. ```tsx ``` -------------------------------- ### Basic Live Chart Setup Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/README.md Demonstrates how to set up a basic live updating chart component. It uses `useSharedValue` for data and value, and `useEffect` with `setInterval` to push new data points. Ensure Reanimated is set up correctly. ```tsx import { useEffect } from "react"; import { View } from "react-native"; import { useSharedValue } from "react-native-reanimated"; import { LiveChart, type LiveChartPoint } from "react-native-livechart"; function LivePrice() { const data = useSharedValue([]); const value = useSharedValue(100); useEffect(() => { const id = setInterval(() => { const next = value.get() + (Math.random() - 0.5) * 2; // Reanimated 4: append in place on the UI thread + set the scalar — never `data.value = ...` data.modify((arr) => { "worklet"; arr.push({ time: Date.now() / 1000, value: next }); if (arr.length > 600) arr.shift(); return arr; }); value.set(next); }, 100); return () => clearInterval(id); }, [data, value]); return ( ); } ``` -------------------------------- ### Configure Scrubbing Tooltip and Crosshair Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/scrubbing.mdx Configure the crosshair and tooltip appearance by passing an object to the `scrub` prop. This example enables the tooltip and sets a custom color for the crosshair line. ```tsx ``` -------------------------------- ### Advanced Threshold Split with Custom Colors and Fill Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/markers-and-references.mdx This example shows an advanced threshold configuration. It disables the default gradient fill, sets custom colors for above and below the threshold, enables a profit/loss fill band, and adds a labeled marker line at the threshold. ```tsx ; ``` -------------------------------- ### Momentum & Degen Configuration Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/api-reference/livechart.mdx Configure momentum-based dot/badge coloring and particle burst effects with screen shake on momentum swings. Includes a callback for when a degen shake starts. ```APIDOC ## Momentum & degen Momentum-based dot/badge coloring. See [Momentum & degen](/guides/momentum-and-degen). Particle burst + screen shake on momentum swings. JS-thread callback when a degen shake starts. ``` -------------------------------- ### Custom Line Chart Tooltip with Animated Text Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/scrubbing.mdx Implement a custom tooltip for line charts using `renderTooltip`. This example binds the time string to an `AnimatedTextInput` via `useAnimatedProps` to ensure smooth, UI-thread updates during scrubbing, preventing JavaScript re-renders. Note the fixed width and monospace font for the text input to accommodate changing values without layout shifts. ```tsx import { View, TextInput, StyleSheet } from "react-native"; import Animated, { useAnimatedProps } from "react-native-reanimated"; import type { TooltipRenderProps } from "react-native-livechart"; const AnimatedTextInput = Animated.createAnimatedComponent(TextInput); function BlueTooltip({ timeStr }: TooltipRenderProps) { // Animate the date text on the UI thread — no JS re-render while scrubbing. const animatedProps = useAnimatedProps(() => { const t = timeStr.get(); return { text: t, defaultValue: t }; }); return ( ); } const styles = StyleSheet.create({ pill: { borderRadius: 10, backgroundColor: "#3323E6", paddingHorizontal: 12, paddingVertical: 6, }, // Fixed width: the TextInput is measured once at mount, and UI-thread text // updates don't re-trigger a layout pass — an auto-width pill would clip. // Use a monospace font (or `tabular-nums`) so every digit is the same width // and the value never outgrows the fixed pill as it changes. text: { width: 72, textAlign: "center", color: "#fff", fontSize: 13, padding: 0, fontVariant: ["tabular-nums"], }, }); ; ``` -------------------------------- ### Custom Candle Chart Tooltip with OHLC Data Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/scrubbing.mdx Implement a custom tooltip for candle mode charts using `renderTooltip`. This example displays OHLC data by binding the `candle` SharedValue to an `AnimatedTextInput` for efficient UI-thread updates. The tooltip can be customized to show specific candle data or remain minimal if the active candle is displayed elsewhere. ```tsx function OhlcTooltip({ candle }: TooltipRenderProps) { const animatedProps = useAnimatedProps(() => { const c = candle.get(); const text = c ? `O ${c.open.toFixed(2)} H ${c.high.toFixed(2)} L ${c.low.toFixed(2)} C ${c.close.toFixed(2)}` : ""; return { text, defaultValue: text }; }); return ( ); } ; ``` -------------------------------- ### Project Utility Commands Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/CLAUDE.md Perform type checking, linting, verification, and building of the library using npm scripts. ```bash npm run typecheck # tsc --noEmit npm run lint # eslint (expo flat config) npm run verify # typecheck + lint + test (all three) npm run build:lib # emit .d.ts only into packages/.../dist/ ``` -------------------------------- ### Rendering Sparklines in a FlatList Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/sparklines.mdx Demonstrates how to integrate the SparklineCell component into a React Native FlatList to display multiple sparklines efficiently. This setup is suitable for watchlists or token grids. ```tsx t.id} renderItem={({ item }) => ( )}/> ``` -------------------------------- ### Run Verification and Individual Development Steps Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/CONTRIBUTING.md Executes the full verification suite (typecheck, lint, test) or individual steps for development and debugging. ```bash npm run verify # typecheck + lint + test (run this before opening a PR) # individual steps npm run typecheck # tsc --noEmit npm run lint # eslint (expo flat config) npm run test # all tests (Jest + jest-expo) npm run test:lib # library tests only npx jest path/to/file # a single test file ``` -------------------------------- ### Running Tests with npm Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/CLAUDE.md Execute all tests in the project, library-specific tests, or a single test file using npm commands. ```bash npm test # run all tests (Jest + jest-expo) npm run test:lib # library tests only npx jest path/to/file # single test file ``` -------------------------------- ### Candlestick Mode Setup Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/candlestick.mdx Configure the LiveChart component for candlestick mode by providing shared values for committed candles and the live candle. Set the candleWidth to define the time bucket size. ```tsx import { useSharedValue } from "react-native-reanimated"; import { LiveChart, type CandlePoint } from "react-native-livechart"; const candles = useSharedValue([]); const liveCandle = useSharedValue(null); ; ``` -------------------------------- ### LiveChart Multi-Series Demo Initialization Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/how-it-works.html Initializes a multi-series chart demo with predefined series definitions, including base values, volatility, and data seeding. Handles dynamic data updates and series visibility toggles. ```javascript const canvas = document.getElementById('multi-canvas'); const togglesEl = document.getElementById('multi-toggles'); const seriesDefs = [ { id: 'A', color: COL.up, base: 60, v: 60, raw: 60, data: [], opacity: 1, visible: true }, { id: 'B', color: COL.accent, base: 50, v: 50, raw: 50, data: [], opacity: 1, visible: true }, { id: 'C', color: COL.purple, base: 40, v: 40, raw: 40, data: [], opacity: 1, visible: true } ]; let t = 0, nextPt = 0; const tw = 24, every = 0.5; // tracked engine state (mirrors the multi-series engine's SharedValues) let displayMin = 35, displayMax = 65, displayWindow = tw, gridPrev = 0; // seed for (let i = -tw - 4; i < 0; i += every) { seriesDefs.forEach(s => { s.raw += (s.base - s.raw) * 0.05 + (Math.random() - 0.5) * 2.2; s.data.push({ time: i, value: s.raw }); }); } // legend chips seriesDefs.forEach(s => { const el = document.createElement('label'); el.className = 'toggle on'; el.innerHTML = ` Series ${s.id}`; el.addEventListener('click', () => { s.visible = !el.classList.contains('on'); el.classList.toggle('on'); }); togglesEl.appendChild(el); }); extraDraws.push((dt) => { t += dt; if (t >= nextPt) { nextPt = t + every; seriesDefs.forEach(s => { s.raw += (s.base - s.raw) * 0.04 + (Math.random() - 0.5) * 2.6 + (Math.random() < 0.05 ? (Math.random() - 0.5) * 8 : 0); s.data.push({ time: t, value: s.raw }); }); } const cutoff = t - tw - 2; seriesDefs.forEach(s => { while (s.data.length > 2 && s.data[1].time < cutoff) s.data.shift(); }); // ENGINE TICK (multi-series) — constants mirror liveChartSeriesEngineTick.ts: // per-series adaptive value lerp ``` -------------------------------- ### Define Chart Segments Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/markers-and-references.mdx Defines pre-market, regular, and after-hours sessions using the `ChartSegment` type. The `mutedColor` property is used to de-emphasize segments when they are not focused. The `divider` and `label` properties can be used to mark the start of a segment. ```tsx import { LiveChart, type ChartSegment } from "react-native-livechart"; const segments: ChartSegment[] = [ // pre-market: left edge → t1 (de-emphasizes to grey when not focused) { to: t1, mutedColor: "rgba(154,160,166,0.4)" }, // regular session, with a dashed divider + label at its start { from: t1, to: t2, mutedColor: "rgba(154,160,166,0.4)", divider: true, label: "Regular" }, // after-hours: t2 → live edge; `active` force-focuses it without a scrub { from: t2, mutedColor: "rgba(154,160,166,0.4)", divider: true, label: "After hours" }, ]; ; ``` -------------------------------- ### Toggle LiveChart Mode Between Line and Candle Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/transitions.mdx Use the `mode` prop on a single `LiveChart` component to animate transitions between line and candlestick views. This requires no additional setup beyond managing the state for the `mode` prop. ```tsx const [mode, setMode] = useState<"line" | "candle">("line"); ; ``` -------------------------------- ### LiveChart Playground Initialization Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/how-it-works.html Initializes an interactive LiveChart playground with various configurable options. Allows users to toggle features like gradients, grids, axes, and more. ```javascript const playSim = new LiveChartSim(document.getElementById('play-canvas'), { height: 340, timeWindow: 26, base: 100, volatility: 1, badge: true, dot: true, grid: true, yaxis: true, fill: true, momentum: true, scrub: true, pulse: true, valueLine: false, degen: false, tradeStream: false, refLine: false }); register(playSim, document.getElementById('play-canvas')); ``` -------------------------------- ### Manage Scrubbing State with onGestureStart/onGestureEnd Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/scrubbing.mdx Use onGestureStart and onGestureEnd to manage a boolean state indicating if a scrub is active. These callbacks run on the JS thread. ```tsx import { useState } from "react"; const [scrubbing, setScrubbing] = useState(false); setScrubbing(true)} onGestureEnd={() => setScrubbing(false)} />; ``` -------------------------------- ### Trades, Markers & References Configuration Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/api-reference/livechart.mdx Configure live trade fills, glyph markers, hover events, hit radius, and custom rendering for markers and tooltips. Also includes settings for reference lines and chart segments. ```APIDOC ## Trades, markers & references Live trade fills for on-chart markers. Glyph markers drawn into the canvas (UI-thread read). Fires when a marker is tapped; `null` on a miss. Tap hit-test radius in px. Render a marker as a custom **React Native** element instead of a built-in Skia glyph — e.g. an `expo-blur` glass badge or any non-Skia view. Return an 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. Custom-rendered markers skip the atlas (no double-draw) and stay crisp at native resolution. Use sparingly — each is its own animated view, whereas built-in glyphs batch into one draw call. See [Custom marker rendering](/guides/markers-and-references#custom-marker-rendering). Render a fully custom scrub tooltip as a **React Native** element instead of the built-in pill — the same idea as `renderMarker`. The chart floats it over the canvas and positions it on the UI thread (per `scrub.tooltipPlacement`); you own the chrome (border radius/color, background are plain RN styles) and content. Bind the [`TooltipRenderProps`](/api-reference/types#tooltiprenderprops) `SharedValue`s to animated text so the value/date update on the UI thread too — smooth, unlike rebuilding the tooltip from the JS-thread `onScrub`. Return `null`/`undefined` to fall back to the built-in pill. **Line mode only** (candle keeps its OHLC stack). See [Custom tooltip](/guides/scrubbing#custom-tooltip-rendertooltip). Reference lines / bands (horizontal line, value band, or time band). Labeled time-range segments (sessions, after-hours, overnight) with **scrub-focus**: at rest the line is one uniform color; while scrubbing — or when a segment is `active` — the focused segment keeps the base color and every other segment is de-emphasized (`mutedColor` / `mutedColors`) by recoloring the line stroke itself, not an overlay. Optional dashed `divider` + `label` mark a segment's edge. See [Segments](/guides/markers-and-references#segments) and [`ChartSegment`](/api-reference/types#chartsegment). Single-series only. Color the line above vs. below a live threshold value — green above, red below by default. The `value` is **always** a `SharedValue`, so the split tracks a live benchmark (break-even / average cost, VWAP, previous close, a peg) on the UI thread without re-rendering. Optionally adds a tinted profit/loss `fill` band and a dashed marker `line`. Supersedes `line.color`/`colors` and segment recoloring for the main stroke while set. See [`ThresholdConfig`](/api-reference/types#thresholdconfig). Single-series, line mode only. ``` -------------------------------- ### Scrub Action Configuration Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/api-reference/types.mdx Optional configuration for the scrub action mode, allowing customization of the action badge icon. ```typescript // Order-ticket mode (scrubAction). Opt-in — default off. interface ScrubActionConfig { icon?: string; // action-badge glyph; default "+" } ``` -------------------------------- ### Set up Gesture Handler Root View Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/installation.mdx Wrap your application or the relevant screen with GestureHandlerRootView to enable gesture handling for charts. This is required for features like scrubbing. ```tsx import { GestureHandlerRootView } from "react-native-gesture-handler"; export default function App() { return ( {/* ...your app */} ); } ``` -------------------------------- ### DotConfig Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/api-reference/types.mdx Shared configuration for live-dots. ```APIDOC ## DotConfig Shared styling properties for live-dots, applicable to both `LiveChart`'s `dot` and `LiveChartSeries`' `dot`. ### `radius` - **Type**: `number` - **Description**: The radius of the color-filled dot. Defaults to `3.5`. ### `ring` - **Type**: `boolean | DotRingConfig` - **Description**: Controls the display of a haloed outer ring around the dot. Can be a boolean to enable/disable, or a `DotRingConfig` object for detailed styling. Defaults to `true` (enabled). ### `show` - **Type**: `boolean` - **Description**: Deprecated. Use `dot={false}` instead to hide the dot. Defaults to `true`. ### `color` - **Type**: `string` - **Description**: The fill color of the dot. Defaults to the line's color. ``` -------------------------------- ### Scrubbing Configuration Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/api-reference/livechart.mdx Configure crosshair scrubbing, selection dots, and related callbacks. ```APIDOC ## Scrubbing Configuration ### Description Configure crosshair scrubbing, selection dots, and related callbacks. ### Parameters #### Body Parameters - **scrub** (boolean | ScrubConfig) - Optional - Crosshair scrubbing on drag. Defaults to `true`. - **selectionDot** (boolean | SelectionDotConfig) - Optional - Dot drawn at the scrub intersection while scrubbing. `false` hides it; pass a config to set `size` / `color` / `ring`, or `{ component }` for a fully custom Skia dot. See [Scrubbing](/guides/scrubbing#selection-dot). Defaults to `true`. - **onScrub** ((point: ScrubPoint | null) => void) - Optional - Fires while scrubbing; `null` when it ends. - **scrubAction** (boolean | ScrubActionConfig) - Optional - **Order-ticket** mode: tap to drop a locked crosshair, drag to fine-tune a **price level**, then press the right-gutter action badge to fire `onScrubAction`. The reported price is the value at the reticle's **Y** (a free level you choose), not the line value at that time. Coexists with `scrub`. Pass a [`ScrubActionConfig`](/api-reference/types#config-objects) for `icon`, colors, `snap`, `text: false` (icon-only), or `timeBadge` (an x-axis date/time pill, off by default). See [Scrubbing](/guides/scrubbing#order-ticket-scrubaction). Single-series only. Defaults to `false`. - **onScrubAction** ((point: ScrubActionPoint) => void) - Optional - JS-thread callback fired when the action badge is pressed. `point.price` is the chosen price level (reticle Y, optionally `snap`-rounded). See [`ScrubActionPoint`](/api-reference/types#scrub-payloads). - **onGestureStart** (() => void) - Optional - JS-thread callback fired once when the user starts scrubbing. Respects `scrub.panGestureDelay` (it fires when the pan activates, not on first touch). - **onGestureEnd** (() => void) - Optional - 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. ``` -------------------------------- ### LiveChart Series Multi-series Initialization Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/how-it-works.html Initializes a LiveChartSim for the hero chart with various configuration options. It also sets up display updates for value, points, and momentum. ```javascript const heroSim = new LiveChartSim(document.getElementById('hero-canvas'), { height: 320, timeWindow: 28, badge: true, dot: true, grid: true, yaxis: true, fill: true, momentum: true, scrub: true, pulse: true, base: 100, volatility: 1.2 }); register(heroSim, document.getElementById('hero-canvas')); const heroVal = document.getElementById('hero-val'), heroPts = document.getElementById('hero-pts'), heroMom = document.getElementById('hero-mom'); extraDraws.push(() => { heroVal.textContent = heroSim.displayValue.toFixed(2); const mom = heroSim._mom || 'flat'; const c = mom === 'up' ? COL.up : mom === 'down' ? COL.down : COL.accent; heroVal.style.color = c; heroPts.textContent = heroSim._visiblePts || '—'; heroMom.textContent = mom; heroMom.style.color = c; }); ``` -------------------------------- ### LiveChart Configuration Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/api-reference/livechart.mdx Configuration options for LiveChart, including theming, layout, text, and accessibility. ```APIDOC ## LiveChart Configuration Options This section details the parameters available for configuring the LiveChart component. ### Theming & Layout - **theme** (string) - Optional - Color scheme. Possible values: "light" | "dark". Defaults to "dark". - **accentColor** (string) - Optional - Primary accent color. Defaults to "#3323E6". - **palette** (Partial) - Optional - Override individual resolved-palette keys. - **metrics** (LiveChartMetricsOverride) - Optional - Sizing & motion tokens for geometry and feel. Namespaced (`badge`, `candle`, `grid`, `motion`, `emptyState`). - **font** (FontConfig) - Optional - Font family, size, weight, and custom typeface for all chart text. - **leftEdgeFade** (boolean | LeftEdgeFadeConfig) - Optional - Soft fade on the left edge. Defaults to true. - **insets** (ChartInsets) - Optional - Padding for the drawing area (`top`, `right`, `bottom`, `left`). - **style** (ViewStyle) - Optional - Container `View` style. ### Text & Accessibility - **formatValue** ((v: number) => string) - Optional - Formatter for value labels (axes, badge, tooltips). - **formatTime** ((t: number) => string) - Optional - Formatter for time labels. - **emptyText** (string) - Optional - Label shown when there are fewer than two samples and `loading` is false. Defaults to "No data". - **accessibilityLabel** (string) - Optional - Accessibility label for the chart container. - **accessibilityRole** (string) - Optional - Accessibility role for the container. Possible values: "image" | "none" | "adjustable" | "summary". Defaults to "image". **Note:** Config-object types like `LineConfig`, `BadgeConfig`, `DegenOptions`, `ReferenceLine`, etc., are documented in the [Types reference](/api-reference/types). ``` -------------------------------- ### Minimal Live Chart with Simulated Data Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/quickstart.mdx This snippet shows how to set up a live chart that updates with a random walk. It uses `useSharedValue` for data and live value, and `useEffect` with `setInterval` to simulate data points. The `data.modify` function is used for efficient, in-place updates on the UI thread. ```tsx import { useEffect } from "react"; import { View } from "react-native"; import { useSharedValue } from "react-native-reanimated"; import { LiveChart, type LiveChartPoint } from "react-native-livechart"; export function LivePrice() { const data = useSharedValue([]); const value = useSharedValue(100); useEffect(() => { const id = setInterval(() => { const time = Date.now() / 1000; // unix seconds const next = value.get() + (Math.random() - 0.5) * 2; // Append the new point in place on the UI thread — the full (growing) // array is never re-cloned across the JS/UI boundary. data.modify((arr) => { "worklet"; arr.push({ time, value: next }); if (arr.length > 600) arr.shift(); return arr; }); value.set(next); }, 100); return () => clearInterval(id); }, [data, value]); return ( ); } ``` -------------------------------- ### Display Loading Placeholder Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/states-and-formatting.mdx Use the `loading` prop to show a breathing-line placeholder while waiting for data. Set it to `true` when data is not yet ready. ```tsx ``` -------------------------------- ### Enable Scrubbing Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/guides/scrubbing.mdx Enable the default scrubbing behavior by passing the `scrub` prop to the LiveChart component. ```tsx ``` -------------------------------- ### LiveChart Configuration Options Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/api-reference/livechart.mdx These parameters control the appearance and behavior of the LiveChart, including axes, grid, time window, and loading states. ```APIDOC ## Axes, grid & range Time-axis labels. Value-axis grid + labels. Edge label floated at the plot's **top**. `true` shows the built-in value label — the chart's current top Y-axis bound, formatted and updated each frame (Robinhood-style high). Pass [`AxisLabelConfig`](/api-reference/types#axislabelconfig) to set `format` / `color` / `position`, or `{ render }` to float a fully custom element instead. See [Axis labels](/guides/theming#axis-edge-labels). Edge label floated at the plot's **bottom**. `true` shows the built-in value label — the chart's current bottom Y-axis bound (the low) — with the same `AxisLabelConfig` knobs and `render` escape hatch as `topLabel`. Grid-line color / width / dash / opacity. Visible window in seconds. Freeze scrolling; resume catches up to real time. Breathing-line loading shell until data is ready. Render once with no per-frame animation loops — engine, degen, trades, candles, markers, pulse, and entry reveal are all disabled, and scrubbing is off. Keeps many small charts cheap when you render a list or grid of sparklines. Frame the data edge-to-edge with `timeWindow` + `nowOverride`. See the [Sparklines guide](/guides/sparklines). Value-lerp speed (0 = frozen, 1 = instant). Tight Y-axis so small moves fill the height. Clamp the Y-axis lower bound at 0. Hard upper bound for the Y-axis range. Right-edge buffer as a fraction of `timeWindow`. Override the engine's "now" (unix seconds) — useful for historical playback. ``` -------------------------------- ### Shared Props and Configuration Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/README.md Details on shared props between LiveChart and LiveChartSeries, and configuration conventions. ```APIDOC ## Shared Props and Configuration ### Description This section covers props common to both `LiveChart` and `LiveChartSeries`, as well as general configuration patterns. ### Shared Props - **font** (any) - Font configuration. - **insets** (any) - Padding or inset values. - **smoothing** (any) - Data smoothing configuration. - **xAxis** (any) - X-axis configuration. - **yAxis** (any) - Y-axis configuration. - **referenceLines** (any) - Configuration for reference lines. - **gridStyle** (any) - Styling for the chart grid. - **palette** (any) - Color palette configuration. - **metrics** (any) - Sizing and motion tokens. - **markers** (any) - Configuration for data markers. - **leftEdgeFade** (any) - Configuration for fading at the left edge of the chart. - **line** (any) - Line styling configuration. - **formatValue** (function) - Function to format value labels. - **formatTime** (function) - Function to format time labels. - **emptyText** (string) - Text to display when the chart is empty. ### Overlay Toggle Convention Many overlay toggles follow a **`boolean | Config`** convention. Pass `true` or omit for defaults, `false` to disable, or an object to customize. This applies to: `badge`, `gradient`, `pulse`, `valueLine`, `scrub`, `yAxis`, `xAxis`, `leftEdgeFade`, `legend`, and `dot`. ### Dot Configuration Both charts share the same `dot` styling, based on a `DotConfig` object with properties like `radius`, `ring` (halo), and `color`. Multi-series extends this with `pulse`, `valueLine`, and `valueLabel`. ### Metrics Configuration Beyond `palette` (color), `metrics` exposes sizing & motion tokens (e.g., badge/candle geometry, grid/motion speeds) with a per-key override model. ``` -------------------------------- ### AI Assistant Connection URL Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/index.mdx This URL can be used to connect AI assistants like Claude or Cursor to these documentation pages for direct searching and citation. ```text https://react-native-livechart.brandtnewlabs.com/mcp ``` -------------------------------- ### Naive Cubic Spline Path Generation (Overshooting) Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/how-it-works.html Generates a cubic spline path using a naive approach that allows for overshooting. This is used for comparison purposes in demos. ```javascript function naiveSplinePath(pts) { const p = new Path2D(); const n = pts.length >> 1; if (n === 0) return p; p.moveTo(pts[0], pts[1]); const P = (i) => [pts[Math.max(0, Math.min(n - 1, i)) * 2], pts[Math.max(0, Math.min(n - 1, i)) * 2 + 1]]; for (let i = 0; i < n - 1; i++) { const [x0, y0] = P(i - 1), [x1, y1] = P(i), [x2, y2] = P(i + 1), [x3, y3] = P(i + 2); // exaggerated tangents -> overshoot const t = 0.9; p.bezierCurveTo( x1 + (x2 - x0) / 6 * t, y1 + (y2 - y0) / 6 * t * 1.8, x2 - (x3 - x1) / 6 * t, y2 - (y3 - y1) / 6 * t * 1.8, x2, y2 ); } return p; } ``` -------------------------------- ### Lerp Follower Visualization Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/how-it-works.html Visualizes the interpolation (lerp) process between a target value and a displayed value. Includes a history trail and visual indicators for target and display values. ```javascript const canvas = document.getElementById('lerp-canvas'); let target = 0.5, display = 0.5, jumpT = 0; const speedSlider = document.getElementById('lerp-speed'); const speedV = document.getElementById('lerp-speed-v'); speedSlider.addEventListener('input', () => speedV.textContent = (+speedSlider.value).toFixed(2)); const hist = []; extraDraws.push((dt) => { jumpT -= dt; if (jumpT <= 0) { jumpT = 1.4 + Math.random(); target = 0.12 + Math.random() * 0.76; } display = lerp(display, target, +speedSlider.value, dt * 1000); hist.push(display); if (hist.length > 200) hist.shift(); const { ctx, w, h } = fitCanvas(canvas, 92); ctx.clearRect(0, 0, w, h); const TY = (v) => 14 + (1 - v) * (h - 28); // target line ctx.strokeStyle = hexA(COL.gold, 0.5); ctx.lineWidth = 1; ctx.setLineDash([5, 4]); ctx.beginPath(); ctx.moveTo(0, TY(target)); ctx.lineTo(w, TY(target)); ctx.stroke(); ctx.setLineDash([]); // history trail ctx.strokeStyle = COL.up; ctx.lineWidth = 2; ctx.beginPath(); hist.forEach((v, i) => { const x = w - (hist.length - i) * (w / 200); i ? ctx.lineTo(x, TY(v)) : ctx.moveTo(x, TY(v)); }); ctx.stroke(); // dots ctx.fillStyle = COL.gold; ctx.beginPath(); ctx.arc(w - 14, TY(target), 5, 0, 7); ctx.fill(); ctx.fillStyle = COL.up; ctx.shadowColor = COL.up; ctx.shadowBlur = 10; ctx.beginPath(); ctx.arc(w - 14, TY(display), 5.5, 0, 7); ctx.fill(); ctx.shadowBlur = 0; }); })(); ``` -------------------------------- ### Decimation Demo for Performance Optimization Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/how-it-works.html This snippet demonstrates decimation to reduce vertex count for dense datasets, improving rendering performance. It includes toggles for decimation and showing dots, and displays the number of points used versus the total. ```javascript const canvas = document.getElementById('decim-canvas'); const decT = document.getElementById('decim-toggle'); const dotT = document.getElementById('dots-toggle'); const countEl = document.getElementById('decim-count'); const savedEl = document.getElementById('decim-saved'); let decimate = true, showDots = true; decT.addEventListener('click', () => { decimate = !decT.classList.contains('on'); decT.classList.toggle('on'); }); dotT.addEventListener('click', () => { showDots = !dotT.classList.contains('on'); dotT.classList.toggle('on'); }); // a dense static dataset — denser than ~2 samples/pixel so min/max-per-column // decimation visibly reduces the vertex count (mirrors a wide timeWindow). const raw = []; let v = 0.5; for (let i = 0; i < 2400; i++) { v += (Math.random() - 0.5) * 0.03; v = Math.max(0.08, Math.min(0.92, v)); raw.push(v); } extraDraws.push(() => { const { ctx, w, h } = fitCanvas(canvas, 200); ctx.clearRect(0, 0, w, h); const padL = 8, padR = 8, padT = 12, padB = 12; const plotW = w - padL - padR, plotH = h - padT - padB; const pts = []; const total = raw.length; const xOf = (i) => padL + (i / (total - 1)) * plotW; const yOf = (i) => padT + (1 - raw[i]) * plotH; if (!decimate) { for (let i = 0; i < total; i++) pts.push(xOf(i), yOf(i)); } else { // Mirror buildLinePoints: keep the lowest + highest sample in each pixel // column (emitted in time order) so the envelope survives at ~2 pts/px. let curCol = -2147483648, minI = 0, maxI = 0, open = false; const flush = () => { const a = minI <= maxI ? minI : maxI, b = minI <= maxI ? maxI : minI; pts.push(xOf(a), yOf(a)); if (b !== a) pts.push(xOf(b), yOf(b)); }; for (let i = 0; i < total; i++) { const col = xOf(i) | 0; if (col !== curCol) { if (open) flush(); curCol = col; minI = i; maxI = i; open = true; } else { if (raw[i] < raw[minI]) minI = i; if (raw[i] > raw[maxI]) maxI = i; } } if (open) flush(); } // fill const path = splinePath(pts); const fill = new Path2D(path); fill.lineTo(pts[pts.length - 2], padT + plotH); fill.lineTo(pts[0], padT + plotH); fill.closePath(); const grad = ctx.createLinearGradient(0, padT, 0, padT + plotH); grad.addColorStop(0, hexA(COL.accent, 0.22)); grad.addColorStop(1, hexA(COL.accent, 0)); ctx.fillStyle = grad; ctx.fill(fill); // line ctx.strokeStyle = COL.accent; ctx.lineWidth = 2; ctx.lineJoin = 'round'; ctx.stroke(path); // dots if (showDots) { ctx.fillStyle = COL.accent2; for (let i = 0; i < pts.length; i += 2) { ctx.beginPath(); ctx.arc(pts[i], pts[i + 1], 1.7, 0, 7); ctx.fill(); } } const n = pts.length >> 1; countEl.textContent = n + ' / ' + total; savedEl.textContent = decimate ? ` · dropped ${total - n} (${Math.round((1 - n / total) * 100)}%)` : ''; }); ``` -------------------------------- ### LiveChart Architecture Map Source: https://github.com/brandtnewlabs/react-native-livechart/blob/main/docs/how-it-works.html An overview of the LiveChart architecture, categorizing components into core, drawing, and math modules. Each module lists its key files and their primary functions. ```javascript const ARCH = [ { name: 'core', color: COL.accent2, badge: 'engine', desc: 'Tick loop + config', nodes: [ ['liveChartEngineTick.ts', 'Pure single-series tick — mutates display state'], ['useLiveChartEngine.ts', 'Hook: useFrameCallback wrapper + SharedValue setup'], ['liveChartSeriesEngineTick.ts', 'Pure multi-series tick — per-series lerp + opacity'], ['useLiveChartSeriesEngine.ts', 'Multi-series frame loop, output buffer ping-pong'], ['resolveConfig.ts', 'Normalizes boolean | Partial into resolved config'], ['multiSeriesLayout.ts', 'Series color/style signature tracking'] ] }, { name: 'draw', color: COL.up, badge: 'skpath', desc: 'Path builders (worklet)', nodes: [ ['line.ts', 'buildLinePoints — data→pixels + decimation'], ['candle.ts', 'buildCandleGeometry — body/wick rects'], ['grid.ts', 'computeGridEntries — nice-interval Y labels'], ['trade.ts', 'Trade stream state machine'], ['markerAtlas.ts', 'Marker glyph atlas'] ] }, { name: 'math', color: COL.gold, badge: 'pure', desc: 'Worklet-safe primitives', nodes: [ ['lerp.ts', 'Frame-rate-independent exponential lerp'], ['spline.ts', 'Fritsch–Carlson monotone cubic (no overshoot)'], ['interpolate.ts', 'Binary search + linear interp for scrubbing'], ['momentum.ts', 'up / down / flat from recent vs lookback'], ['color.ts', 'hex→rgb, RGB lerp (worklet-safe)'], ['range.ts', 'Y-range with margins'], ['degenTick.ts', 'Particle ring buffer (spawn/update/expire)'], ['referenceLines.ts', 'Collect + classify reference values'], ['intervals.ts', ```