Sparkline

Free · MITReact Native · Expo

A small line or area chart that draws itself in and morphs to new data; press and drag to scrub, and a dot rides the curve while the line beyond your finger dims and every point ticks.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add https://goodui.dev/native/r/sparkline.json

Then run npx expo install --fix so react-native-gesture-handler, react-native-reanimated, react-native-svg match your Expo SDK. Files land in components/goodui-native/sparkline.

Usage

import { useState } from "react";
import { Sparkline } from "@/components/goodui-native/sparkline/sparkline";
import { RollingNumber } from "@/components/goodui-native/rolling-number/rolling-number";

export function Balance({ history, dates }: { history: number[]; dates: string[] }) {
  const [point, setPoint] = useState<number | null>(null);
  const shown = point ?? history[history.length - 1];
  return (
    <>
      <RollingNumber value={shown} size="title1" format={{ style: "currency", currency: "EUR" }} />
      <Sparkline
        data={history}
        labels={dates}
        height={96}
        accessibilityLabel="Balance, last 30 days"
        formatValue={(v) => `€${v.toFixed(2)}`}
        onScrub={(p) => setPoint(p?.value ?? null)}
      />
    </>
  );
}

When to use

Use it for
  • A trend next to a number: portfolio value, revenue this week, heart rate, a stock row.
  • Places where the exact value at a point matters now and then: scrubbing reveals it without an axis.
  • Switching ranges (1D, 1W, 1M) with the same number of points: the line morphs instead of redrawing.
Reach for something else
  • Charts that need axes, gridlines, legends or several series: use a full charting library.
  • Progress toward a goal: use progress-ring.
  • A single changing number: use rolling-number.

Examples

Line only, fixed tone, baseline

<Sparkline data={heartRate} variant="line" tone="accent" showBaseline height={48} />

Static row chart

<Sparkline data={prices} height={32} scrub={false} showEndDot={false} />

API reference

PropTypeDefault
data*
Values, oldest first. Two or more points draw a line.
number[]—
height
Height in points. The width fills the parent. Default 64.
number64
variant
`area` adds a soft fill under the line; `line` is the stroke alone.
"line" | "area""area"
tone
`trend` is green when the last value is at or above the first and red when below.
"success" | "danger" | "accent" | "neutral" | "trend""trend"
strokeWidth
Stroke width. Default 2.
number2
scrub
Press and drag (or hover with a mouse) to read values. Default `true`.
booleantrue
formatValue
Formats a value for the scrub label and screen readers. Defaults to the locale number format.
(value: number, index: number) => string—
labels
One label per point, for example dates, shown under the value while scrubbing.
string[]—
onScrub
Called when the scrubbed point changes, and with `null` when scrubbing ends.
(point: SparklinePoint | null) => void—
showBaseline
A dashed line at the first value, so gains and losses read at a glance.
booleanfalse
showEndDot
A dot on the latest value. Default `true`.
booleantrue
haptic
Haptic for every point the scrub crosses. `false` turns it off.
HapticName | false"select"
animateOnMount
Draw the line in when it first appears. Default `true`.
booleantrue
accessibilityLabel
What the chart shows, for example "Portfolio, last 7 days". Same as the pack's `chart` string.
string—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • On first appearance the line draws itself in over 720 ms and the area fades in behind it.
  • New data with the same number of points morphs on the smooth spring; a different count draws in again.
  • While scrubbing a dot rides the curve under the finger, a guide line follows it, and the line beyond the finger dims to a muted copy.
  • Reduced motion: no drawing or morphing; the chart fades in and changes at once.
  • Haptic: select for every point the finger crosses while scrubbing (configurable with the haptic prop); none on hover or mount
  • Path, dot, guide and dimming are animated props on the UI thread; the finger never causes a React render per frame.
  • React renders once each time the scrub crosses into a new point, to update the label.
  • Built for tens to a few hundred points; downsample longer series first.

Accessibility

  • One adjustable element: the label summarises the range (from, to, high, low).
  • Swiping up or down with a screen reader steps through the points and announces each value and label.
  • The scrub label is hidden from screen readers; the value is exposed through accessibilityValue.
  • On the web it is a focusable slider whose value is the scrubbed point (else the latest); arrow keys step through the points.

Notes for AI

  • data is plain numbers, oldest first; pass labels for dates instead of objects.
  • Use onScrub to drive a headline number (rolling-number) and fall back to the latest value on null.
  • The width fills its parent; set height only.
  • Wrap it in a scroll view freely: vertical drags scroll, horizontal drags scrub.