> ## 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.

# Multi-series

> Render several live series with a toggleable legend and per-series dots.

`LiveChartSeries` draws multiple line series that share an axis, time window, and crosshair.
Pass a `SharedValue<SeriesConfig[]>`.

<Note>
  **Live example:**
  [`app/demo/multi-series.tsx`](https://github.com/brandtnewlabs/react-native-livechart/blob/main/app/demo/multi-series.tsx)
  in the example app.
</Note>

<Frame caption="Multiple live series with a toggleable legend">
  <video autoPlay loop muted playsInline controls poster="/media/multi-series.jpg" src="https://mintcdn.com/brandtnewlabs/3aK_iydbC8Ff9g3s/media/multi-series.mp4?fit=max&auto=format&n=3aK_iydbC8Ff9g3s&q=85&s=769a8d0275db83c160c8aee5bb5a5169" data-path="media/multi-series.mp4" />
</Frame>

```tsx theme={null}
import { useSharedValue } from "react-native-reanimated";
import { LiveChartSeries, type SeriesConfig } from "react-native-livechart";

const series = useSharedValue<SeriesConfig[]>([
  { id: "btc", label: "BTC", color: "#f7931a", data: [], value: 0 },
  { id: "eth", label: "ETH", color: "#627eea", data: [], value: 0 },
]);

<LiveChartSeries series={series} legend dot />;
```

Update the series the same way you feed a single chart — append points in place with
`series.modify(...)` (mutating each entry's `data` array and `value`), or replace the whole array
with `series.set(...)`. Avoid `series.value = ...`.

For a dataset or unit change (for example Price → Market Cap), replace each
series' history array and live value together. Replacement entries reach drawing
on the next measured engine frame, alongside that frame's Y range and tips.
History arrays remain shared, so this does not isolate in-place edits to existing
points. Normal live appends with `series.modify(...)` remain supported. Use
`snapKey` when the replacement should settle immediately instead of easing with
the configured `smoothing`.

## Series options

Each `SeriesConfig` supports:

| Field | Purpose |
| - | - |
| `id` | Stable identifier (required) |
| `data` | `LiveChartPoint[]` history (required) |
| `value` | Latest live value for interpolation (required) |
| `color` | Line color (falls back to the built-in series palette) |
| `label` | Legend chip text |
| `visible` | Show/hide the series (default `true`) |
| `style` | `"solid"` or `"dashed"` (with `intervals`) |
| `curve` | `"monotone"` (default) smooth spline or `"linear"` straight segments |
| `simplify` | Per-series path tolerance in screen pixels; overrides `line.simplify` |
| `strokeWidth` | Per-series width override |
| `glow` | Soft glow behind the line |
| `kind` | `"outcome"` (default) or `"derived"` (subdued/dashed chip) |

Set `line={{ simplify: 1 }}` on `LiveChartSeries` to denoise every rendered
path by roughly one screen pixel. A series can choose a different tolerance, or
set `simplify: 0` to keep its exact point path. The shared axis, scrub values,
and live dots continue to use the original series data.

## Dimming replacement data

For a timeframe change, retain the current series while the next range loads,
then set one UI-thread opacity multiplier for every plotted series:

```tsx theme={null}
const seriesOpacity = useSharedValue(1);

seriesOpacity.set(isLoadingReplacement ? 0.5 : 1);

<LiveChartSeries series={series} seriesOpacity={seriesOpacity} />;
```

The multiplier covers strokes, value lines, and inline value labels. It does not
dim axes, the legend, reference lines, or scrub UI. Live dots and pulse rings
fade completely out while the multiplier is below `1`, then fade back in after
the plotted series returns to full opacity.

## Legend

The legend renders toggle chips by default. Configure it with the `legend` prop, and listen for
taps with `onSeriesToggle`:

```tsx theme={null}
<LiveChartSeries
  series={series}
  legend={{ position: "top", compact: false }}
  onSeriesToggle={(id, visible) => {
    // Persist the toggle back onto the series shared value.
    series.set(series.get().map((s) => (s.id === id ? { ...s, visible } : s)));
  }}
/>
```

## Per-series dots

The `dot` prop controls the live dots, their pulse, optional static glow,
value lines, and inline labels. Each dot has a crisp contrasting outer ring (in
the chart background color) by default, matching the single-series live dot so
overlapping series stay legible. The soft glow remains opt-in:

```tsx theme={null}
<LiveChartSeries
  series={series}
  dot={{
    radius: 3.5,
    ring: true,
    glow: { radius: 7, blur: 5, opacity: 0.14 },
    pulse: false,
    valueLine: true,
    valueLabel: true,
  }}
/>
```

Tune or turn off the backing ring, tune the independent glow, hide the dots, or
override the fill color:

```tsx theme={null}
<LiveChartSeries
  series={series}
  // thicker, tinted backing ring
  dot={{ ring: { color: "#0b0b0f", width: 3 } }}
  // ...or a lower-opacity glow in each series color
  // dot={{ glow: { opacity: 0.1, blur: 4 } }}
  // ...or a flat circle (no backing ring)
  // dot={{ ring: false }}
  // ...or hide the dots entirely (lines + labels still render)
  // dot={{ show: false }}
/>
```

See the full prop list in the [`LiveChartSeries` reference](/api-reference/livechart-series).

## Per-series scrub tooltip

`LiveChartSeries` keeps its historical guide-only scrub by default. Opt into the
Morfi market-detail readout with `scrub.seriesTooltip`: a dashed vertical guide,
a top-centred bucket range, and one pill plus intersection dot for every visible
series. Close values stack instead of overlapping, wide pills flip to the
guide's left near the right edge, and hidden legend series are omitted.

<Note>
  This feature is opt-in and backward compatible. `scrub={true}`,
  `scrub={{}}`, and `scrub={{ seriesTooltip: false }}` all keep the existing
  guide-only multi-series behavior. The existing `scrub.tooltip` property is
  still the master switch; setting it to `false` also suppresses the per-series
  pills.
</Note>

Enable the default tooltip treatment with:

```tsx theme={null}
<LiveChartSeries
  series={series}
  scrub={{ seriesTooltip: true }}
/>
```

Pass an object instead of `true` to customize formatting and appearance:

```tsx theme={null}
<LiveChartSeries
  series={series}
  scrub={{
    seriesTooltip: {
      formatSeriesValue: (value, seriesId) => {
        "worklet";
        return seriesId.startsWith("probability")
          ? `${(value * 100).toFixed(1)}%`
          : `$${value.toFixed(2)}`;
      },
      formatTimeRange: (from, to) => {
        "worklet";
        return `${formatTime(from)} – ${formatTime(to)}`;
      },
      maxLabelChars: 14,
    },
  }}
/>
```

`formatSeriesValue` and `formatTimeRange` run on the UI thread; keep them
worklet-safe, just like the chart's `formatValue` and `formatTime`. Omit
`bucketSeconds` to infer the range width from the first visible series. The
range end is always clamped to the chart's current-time anchor, so an
in-progress live bucket never displays a future end time.

Set `alwaysShow` to keep the value pills at the visible endpoints while the
user is not touching the chart. Idle mode deliberately hides the guide and
time pill; scrubbing restores the full readout.

```tsx theme={null}
<LiveChartSeries
  series={series}
  scrub={{
    seriesTooltip: {
      alwaysShow: true,
      guideColor: "#f59e0b",
      guideDashPattern: [3, 3],
      timePillBackground: "#1e293b",
      seriesPillBackground: "#1e293b",
      seriesPillLabelColor: "#94a3b8",
      seriesPillValueColor: "#f8fafc",
      seriesPillRadius: 10,
      intersectionDotSize: 8,
    },
  }}
/>
```

The [multi-series demo](https://github.com/brandtnewlabs/react-native-livechart/blob/main/app/demo/multi-series.tsx)
has live toggles for the pills, pinned mode, and styling.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.