Sparkline
Free · MITReact Native · ExpoA 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.
Installation
$ npx shadcn@latest add https://goodui.dev/native/r/sparkline.jsonThen 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
- 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.
- 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
| Prop | Type | Default |
|---|---|---|
| data* Values, oldest first. Two or more points draw a line. | number[] | — |
| height Height in points. The width fills the parent. Default 64. | number | 64 |
| 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. | number | 2 |
| scrub Press and drag (or hover with a mouse) to read values. Default `true`. | boolean | true |
| 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. | boolean | false |
| showEndDot A dot on the latest value. Default `true`. | boolean | true |
| 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`. | boolean | true |
| 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.
Related
A number whose digits roll in the direction of change, with digits that open and close in place and currency signs that glide instead of jumping.
A circular progress ring that fills on a spring, laps itself past 100 % with a gap that lifts the lap off the ring, and bounces once with a success haptic when it completes.
Two to five views of the same thing with an elastic highlight that stretches toward the new segment, can be dragged, and ticks at every boundary. Includes a billing toggle.