# Installation (/docs/charts/installation)

<InstallTabs
  commands={[
    { verb: "add", packages: ["@delacour/react-native-charts"] },
    {
      verb: "expo",
      packages: [
        "react-native-skia",
        "react-native-gesture-handler",
        "react-native-reanimated",
        "react-native-worklets",
      ],
    },
  ]}
/>

The package ships raw `.ts` and `.tsx`, and every native module it draws with is a **peer**. Two
copies of Skia or Reanimated register twice and fail at runtime, which is why they are not
dependencies.

<Callout type="warn" title="Do not install native modules with your package manager">
`npm install react-native-reanimated` (or any package manager's add) fetches the newest release, which on any older SDK is a package
that fails at the linker rather than at install time. `expo install` asks the SDK what it supports.
</Callout>

## Peer dependencies [#peer-dependencies]

| Package | Range | Needed for |
| --- | --- | --- |
| `react-native-skia` | `>=3` | the canvas, paths and text |
| `react-native-reanimated` | `>=4` | path morphing and the scrub's shared values |
| `react-native-gesture-handler` | `>=2.28` | the scrub's pan and the pie's tap |
| `react-native-worklets` | `>=0.5` | the UI-thread functions Reanimated 4 runs |
| `react` | `>=19` | |
| `react-native` | `>=0.81` | |

`d3-scale` and `d3-shape` are the package's only runtime dependencies. They are pure JavaScript
and arrive on their own.

## The gesture root [#the-gesture-root]

Your app needs a `GestureHandlerRootView` around everything that takes touches. The package does
not render one — a nested root is dead weight, and every React Native app that handles touch
already has one. If you also use `@delacour/react-native-ui`, its `DelacourProvider` is that root.

```tsx title="app/_layout.tsx"
import { GestureHandlerRootView } from "react-native-gesture-handler";

export default function RootLayout() {
  return <GestureHandlerRootView style={{ flex: 1 }}>{children}</GestureHandlerRootView>;
}
```

<Callout type="warn">
Without the gesture root a scrub never activates and a pie never reports a tap. Silently — no
error, no warning.
</Callout>

## The worklets plugin [#the-worklets-plugin]

Reanimated 4 compiles its UI-thread functions with the `react-native-worklets/plugin` Babel
plugin. `babel-preset-expo` adds it for you once `react-native-worklets` is installed; a bare React
Native app lists it **last** in `babel.config.js`.

```js title="babel.config.js"
module.exports = {
  presets: ["module:@react-native/babel-preset"],
  plugins: ["react-native-worklets/plugin"],
};
```

## Verify [#verify]

A chart needs a height. The root fills whatever view it is placed in and measures itself with
`onLayout`, so give it a box.

```tsx
import { CartesianChart, ChartLine } from "@delacour/react-native-charts";
import { View } from "react-native";

const rows = [
  { day: "Mon", revenue: 120 },
  { day: "Tue", revenue: 180 },
  { day: "Wed", revenue: 150 },
  { day: "Thu", revenue: 210 },
  { day: "Fri", revenue: 240 },
];

<View style={{ height: 240 }}>
  <CartesianChart data={rows} xKey="day" yKeys={["revenue"]}>
    <ChartLine color="#0A84FF" curve="monotone" yKey="revenue" />
  </CartesianChart>
</View>;
```

A chart in a zero-height view draws nothing and says nothing: the plot rect has no area, and
`ready` on the context is `false`.

## Fonts [#fonts]

No font ships with the package. `useSystemFont` resolves one from the families the operating
system already has, so an axis is legible with no `expo-font` and no config plugin. Call it
**above** the chart and pass the result in — [Composition](/docs/charts/composition) explains why
it cannot be called inside.

```tsx
import { CartesianChart, ChartXAxis, useSystemFont } from "@delacour/react-native-charts";

const font = useSystemFont(undefined, 12);

<CartesianChart data={rows} font={font} xKey="day" yKeys={["revenue"]}>
  <ChartXAxis color="#8E8E93" />
</CartesianChart>;
```

iOS's system font is not Android's, so the same labels measure differently and the plot rect
differs by a few points across platforms. An app that needs identical layout on both loads its own
font and passes that instead.
