npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-pure-chart

v2.0.2

Published

Charts built from nothing but <View> and <Text>. Zero dependencies, no native modules, no SVG, no Skia.

Readme

react-native-pure-chart

Charts built from nothing but <View> and <Text>. Zero dependencies. No native modules. No SVG, no Skia.

npm npm downloads license types dependencies

react-native-pure-chart draws every axis, line, bar and pie slice with plain React Native core components — rotated Views with transformOrigin, circular clipping, and the Animated API. Nothing to link, nothing to compile.

Why pure?

  • npm install and you're done. No pod install, no config plugin, no native linking — works in Expo Go, bare RN, and react-native-web.
  • Survives every React Native upgrade. Only core components are used, so there is no native module to break on the next RN release.
  • New Architecture native. Built for RN 0.76+ / Fabric; animations run on the native driver (transform/opacity only).

Quick start

import { LineChart } from 'react-native-pure-chart';

<LineChart data={[30, 200, 170, 250, 10]} />;

That's a full chart: width fills the parent, nice-number ticks, compact 1.2K-style labels, and a 500 ms entrance animation — all defaults.

Installation

npm install react-native-pure-chart
# or
yarn add react-native-pure-chart

Requires React Native 0.73+ (for transformOrigin). No other setup.


Gallery

Every image below is a real screenshot from example/ on an iOS simulator — the code under each one is what produced it.

Line charts

Multi-series + legend

<LineChart data={revenue} height={200} />

Smooth curve + area fill

<LineChart data={traffic} curve="monotone" area />

Step curve, thick, no dots

<LineChart
  data={traffic}
  curve="step"
  strokeWidth={3}
  showDataPoints={false}
/>

null values break the line

<LineChart data={[50, null, null, 90, 40, 70, 20, 60]} />

Custom value labels

<LineChart
  data={traffic}
  curve="monotone"
  renderValueLabel={(e) => <Pill value={e.value} />}
/>

Built-in tooltip + guide line

// tap, or drive it yourself
<LineChart data={revenue} selectedIndex={3} />

Bar charts

Grouped

<BarChart data={platforms} barRadius={5} />

Stacked

<BarChart data={platforms} stacked barRadius={5} />

Horizontal

<BarChart data={monthly} horizontal barRadius={5} />

Negative values

<BarChart data={[12, -8, 25, -15, 30, 5]} barRadius={5} />

Selection dims the rest

<BarChart data={platforms} selectedIndex={2} />

Stacked + horizontal + dark

<BarChart data={platforms} stacked horizontal theme="dark" />

Pie & donut

Pie + legend

<PieChart data={budget} size={200} />

Donut + center label

<PieChart
  data={budget}
  innerRadius="60%"
  renderCenterLabel={() => <Total />}
/>

On-slice labels + padAngle

<PieChart data={budget} padAngle={2} sliceLabel="percentage" />

Dark donut

<PieChart data={budget} innerRadius="60%" theme="dark" />

Dark mode

One prop. Pass useColorScheme() straight through and every axis, grid line, label and tooltip flips with it.

import { useColorScheme } from 'react-native';

const scheme = useColorScheme();

<LineChart data={revenue} theme={scheme === 'dark' ? 'dark' : 'light'} />;

API

Runtime exports: LineChart, BarChart, PieChart, DEFAULT_PALETTE, formatCompact. Every prop and payload type is exported too (LineChartProps, PressEvent, ChartTheme, …).

Data

type ChartData =
  | readonly (number | null | DataPoint)[] // one series
  | readonly Series[]; // many

interface DataPoint {
  value: number | null; // null = missing
  label?: string; // x-axis label
  color?: string; // per-point override
  extra?: unknown; // returned untouched in press events
}

interface Series {
  data: readonly (number | null | DataPoint)[];
  name?: string; // legend + tooltip
  color?: string; // defaults to palette[seriesIndex]
}

Numbers, nulls and DataPoint objects can be mixed freely in one array. Pie charts take PieSliceDatum[]{ value, label?, color?, extra? }.

Colors

Nothing is hard-coded. Colors come from four levels, most specific first:

// 1. the whole chart — replaces the auto-assigned palette
<BarChart data={data} palette={['#FF6B6B', '#4ECDC4', '#FFD93D']} />

// 2. one series / one pie slice
<LineChart data={[{ name: 'Sales', data: [30, 90, 50], color: '#FF6B6B' }]} />
<PieChart data={[{ value: 50, label: 'Marketing', color: '#FF6B6B' }]} />

// 3. one bar — highlight a single value
<BarChart data={[30, { value: 90, color: '#FF6B6B' }, 50]} />

// 4. line dots, independently of the line
<LineChart data={data} showDataPoints={{ color: '#111827', radius: 5 }} />

The palette cycles when there are more series than colors, so it never runs out. Extend the built-in one instead of retyping it:

import { DEFAULT_PALETTE } from 'react-native-pure-chart';

<BarChart data={data} palette={['#FF6B6B', ...DEFAULT_PALETTE]} />;

Two limits worth knowing: a per-point color applies to grouped and single-series bars only — stacked segments take the series color — and line segments always use the series color, so a single point can't recolor the line it sits on. Axis, grid, label and tooltip colors are not part of the palette; they come from theme.

LineChart

| Prop | Type | Default | Description | | --- | --- | --- | --- | | data | ChartData | — | Numbers, {value, label} objects, or an array of series | | strokeWidth | number | 2 | Line thickness (no upper limit) | | curve | 'linear' \| 'step' \| 'monotone' | 'linear' | 'monotone' draws a smooth curve that never overshoots the data | | area | boolean \| {opacity} | false | Fill between the line and the zero baseline (default opacity 0.15) | | showDataPoints | boolean \| {radius, color} | true | Point markers. Default radius max(3, strokeWidth * 1.5) | | missingValues | 'break' \| 'interpolate' \| 'zero' | 'break' | How null values are treated | | spacing | number | auto | Exact px between points. Auto mode fills the width, but never squeezes below 40px — the chart grows and scrolls instead | | scrollable | boolean | true | Horizontal scroll when content overflows | | initialScroll | 'start' \| 'end' | 'start' | Initial scroll position | | legend | boolean \| LegendOptions | auto | Shown when there are 2+ series and at least one is named | | tooltip | boolean \| TooltipOptions | true | Built-in tooltip + selection guide | | onPointPress | (e: PressEvent) => void | — | Tap on the chart (nearest point wins) | | selectedIndex / onSelectionChange | number \| null | — | Controlled selection | | renderDataPoint | (e: PressEvent) => ReactNode | — | Replace point markers | | renderValueLabel | (e: PressEvent) => ReactNode | — | Always-visible label above points | | xAxis / yAxis | see Axes | — | Ticks, grid, formatters |

BarChart

| Prop | Type | Default | Description | | --- | --- | --- | --- | | data | ChartData | — | Same shapes as LineChart | | barWidth | number | auto | Bar width. Auto mode fills the width, but grows and scrolls instead of rendering sliver bars for many categories | | barRadius | number | 4 | Corner radius on the value end | | barGap | number | 2 | Gap between bars in a group | | groupGap | number | auto | Gap between categories | | stacked | boolean | false | Stack multi-series values (positives up, negatives down) | | horizontal | boolean | false | Horizontal bars (yAxis still configures the value axis) | | legend | boolean \| LegendOptions | auto | Same rule as LineChart | | scrollable / initialScroll | | true / 'start' | Vertical bars only — horizontal bars always fit their height | | tooltip | boolean \| TooltipOptions | true | Selection also dims the other categories | | onPointPress / selectedIndex / onSelectionChange | | | Same as LineChart | | renderValueLabel | (e: PressEvent) => ReactNode | — | Label above each bar | | xAxis / yAxis | see Axes | — | yAxis is always the value axis |

Negative values are supported — bars grow down from the zero baseline, and stacks split at it.

PieChart

| Prop | Type | Default | Description | | --- | --- | --- | --- | | data | PieSliceDatum[] | — | {value, label?, color?, extra?} per slice | | size | number | 200 | Diameter in px | | innerRadius | number \| '60%' | 0 | Donut hole — px, or a % of the radius | | holeColor | string | theme background | Hole fill (opaque — see Limitations) | | startAngle | number | 0 | Degrees clockwise from 12 o'clock | | padAngle | number | 0 | Gap between slices in degrees | | sliceLabel | 'none' \| 'label' \| 'value' \| 'percentage' \| (e) => string | 'none' | On-slice labels | | legend | boolean \| LegendOptions | true | Lists every labeled slice | | focusOnPress | boolean | true | Tapped slice slides outward | | onSlicePress | (e: SlicePressEvent) => void | — | Tap on a slice | | selectedIndex / onSelectionChange | number \| null | — | Controlled selection | | renderCenterLabel | (selected: SlicePressEvent \| null) => ReactNode | — | Donut center content |

PieChart sizes itself with size — it does not take width, height or padding.

Common props

Every chart accepts:

| Prop | Type | Default | Description | | --- | --- | --- | --- | | width | number | parent width | Measured with onLayout when omitted | | height | number | 220 | Plot height (pies use size) | | padding | number \| {top, right, bottom, left} | {top: 12, right: 12, bottom: 0, left: 0} | Inner padding of the plot area | | palette | string[] | built-in 10 colors | Auto-assigned series/slice colors — exported as DEFAULT_PALETTE | | theme | 'light' \| 'dark' \| ChartTheme | 'light' | Pass useColorScheme() output directly | | animate | boolean \| AnimationConfig | true | {duration, delay, easing, type: 'grow' \| 'fade' \| 'none'} | | style, testID, accessibilityLabel | | | Standard RN props (a11y label auto-generated) |

The default entrance (type: 'grow') is chart-aware: lines draw left to right, bars grow from the baseline with a stagger, pies sweep clockwise. One Animated.Value drives the whole chart on the native driver.

Axes

yAxis configures the value axis (including on horizontal bars), xAxis the category axis.

| YAxisOptions | Type | Default | | --- | --- | --- | | show | boolean | true | | tickCount | number | 5 | | min / max | number | nice-number bounds from the data | | formatLabel | (value, index) => string | formatCompact | | showGridLines | boolean | true | | gridLineStyle | 'solid' \| 'dashed' | 'dashed' | | showAxisLine | boolean | false | | position | 'left' \| 'right' | 'left' (ignored by horizontal bars) | | labelStyle | TextStyle | — |

| XAxisOptions | Type | Default | | --- | --- | --- | | show | boolean | true (auto-hidden when no point has a label) | | interval | number \| 'auto' | 'auto' — thins labels to fit the width | | formatLabel | (label, index) => string | identity | | showAxisLine | boolean | true | | showGridLines | boolean | false | | labelStyle | TextStyle | — |

Theme, legend, tooltip, events

interface ChartTheme {
  gridColor?: string;
  axisColor?: string;
  labelColor?: string;
  tooltipBackgroundColor?: string;
  tooltipTextColor?: string;
  /** Only used as the donut hole fill — charts never paint their background. */
  backgroundColor?: string;
}

interface LegendOptions {
  position?: 'top' | 'bottom'; // default 'bottom'
  labelStyle?: TextStyle;
}

interface TooltipOptions {
  render?: (e: PressEvent) => ReactNode; // replace it entirely
  formatValue?: (value: number) => string; // default formatCompact
  dismissOnTapOutside?: boolean; // default true
}

interface PressEvent {
  point: DataPoint;
  value: number | null;
  label?: string;
  index: number; // index within the series
  seriesIndex: number;
  seriesName?: string;
  position: { x: number; y: number }; // px, relative to the chart
}

interface SlicePressEvent {
  slice: PieSliceDatum;
  index: number;
  value: number;
  percentage: number; // 0..1
}

Guides

Handling missing data

null values are first-class citizens in line charts:

  • 'break' (default) — the line is interrupted, like recharts' connectNulls={false}. An isolated point still gets its dot.
  • 'interpolate' — gaps are bridged linearly; synthetic points get no markers
  • 'zero' — nulls are treated as 0

Bars always render null as an empty slot (the category label remains).

Controlled selection

Omit selectedIndex and the chart manages selection itself. Pass it and you own it — useful for syncing a chart with an external list:

const [selected, setSelected] = useState<number | null>(null);

<BarChart
  data={data}
  selectedIndex={selected}
  onSelectionChange={setSelected}
  onPointPress={(e) => console.log(e.seriesName, e.label, e.value)}
/>;

Custom tooltips

<BarChart
  data={data}
  tooltip={{
    render: (e) => (
      <View style={styles.tip}>
        <Text>
          {e.seriesName}: {e.value}
        </Text>
      </View>
    ),
  }}
/>

Axis formatting

formatCompact (exported) is the default y-axis formatter: 1234 → '1.2K', 2500000 → '2.5M'. Compose it for currencies or percentages:

import { formatCompact } from 'react-native-pure-chart';

<LineChart
  data={revenue}
  yAxis={{ tickCount: 4, formatLabel: (v) => `$${formatCompact(v)}` }}
  xAxis={{ interval: 2 }}
/>;

Many points

Auto sizing never crams points closer than 40px (or bars thinner than 12px). Past that the chart grows wider than its viewport and scrolls horizontally, with the y-axis labels staying put. Set scrollable={false} to squeeze everything into the width instead, or initialScroll="end" to open on the most recent data.

Limitations

Honesty section — the price of the zero-dependency concept:

  • Smooth curves cost Views. curve="monotone" approximates the curve with short line segments (budgeted at ~400 per series). For very dense series prefer 'linear'.
  • Donut holes are opaque. The hole is a colored circle overlay, so donuts don't work on top of images/gradients. Set holeColor to match your background.
  • Large datasets: each point/segment is a View. A few hundred points are fine; for thousands of points use an SVG- or Skia-based library instead.

Migrating from 0.x

Version 2 is a complete rewrite with no backward compatibility. The old <PureChart type="..." /> component is gone — import charts directly:

| 0.x | 2.x | | --- | --- | | <PureChart type="line" /> | <LineChart /> | | <PureChart type="bar" /> | <BarChart /> | | <PureChart type="pie" /> | <PieChart /> | | data={[{x, y}]} | data={[{value, label}]} | | data={[{seriesName, data, color}]} | data={[{name, data, color}]} | | gap | spacing | | lineThickness (max 10) | strokeWidth (unlimited) | | numberOfYAxisGuideLine | yAxis={{ tickCount }} | | yAxisSymbol | yAxis={{ formatLabel }} | | showXAxisLabel / showYAxisLabel | xAxis={{ show }} / yAxis={{ show }} | | showEvenNumberXaxisLabel | xAxis={{ interval }} | | initialScrollPosition + initialScrollTimeOut | initialScroll: 'start' \| 'end' | | onPress(index) | onPointPress(event) — full event object | | customValueRenderer | renderValueLabel | | defaultColumnWidth / defaultColumnMargin | barWidth / groupGap | | primaryColor / colors | Series.color / palette |

Development

npm install          # library dev deps
npm test             # unit tests for the core + component tests
npm run typecheck

cd example && npm install
npm start            # gallery app against the local ../src (live editing)
npm run start:npm    # gallery app against the published npm package

The example/ app is a gallery of every chart variant, including artifact stress tests (1° pie slices, high-contrast neighbors, missing data). It runs in two modes: npm start resolves the library from ../src for live development, while npm run start:npm resolves the version installed from npm — use it to smoke-test a release exactly as consumers get it. example/screens/ShotScreen.tsx is the reproducible source of every image in docs/ — flip SHOT_MODE in example/App.tsx, pick a group, screenshot.

License

MIT