Library / SDK
FormidableLabs/victory-native-xl avatar
FormidableLabs/victory-native-xl

Victory Native XL: React Native charts drawn with D3, Skia and Reanimated

A charting library for React Native with a focus on performance and customization.

1,236 stars113 forksTypeScriptLicense varies

At a glance

What is it?
Victory Native XL is a charting library for React Native that renders through Skia and drives interaction with Reanimated and Gesture Handler rather than through React Native's own view layer. It suits a mobile team that needs charts that keep up with gestures, and it asks you to install three peer dependencies before the library itself.
Who is it for?
Victory Native XL fits a React Native application whose charts need to stay smooth under touch, because the rendering goes through Skia and the animation work is off the JavaScript thread in Reanimated, which is the whole point of the library. Skip it if your charts are static or if your app is on the web, since the same project ships a separate web entry point and there is a `index.web.tsx` file in the example for that case.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three rendering layers, and you install all of them

Victory Native XL is a charting library for React Native, and the description at the top of the repository is the whole pitch: high-performance charts powered by D3, Skia and Reanimated for React Native.

Each of those three names does a specific job. D3 supplies the maths of the chart, the scales, the shapes and the layout, and it supplies the vocabulary the rest of the charting world uses. Skia is the rendering engine that actually draws the primitives, and it is the reason this library is fast, because Skia draws on the GPU with its own renderer rather than going through React Native's view system. Reanimated handles the animation and gesture work on the UI thread, so a pan or a zoom does not have to cross the bridge to the JavaScript thread to keep up with a finger.

The installation order in the documentation makes the dependency structure clear. You start by installing the peer dependencies of `victory-native`, which are React Native Reanimated, Gesture Handler and Skia:

shell
yarn add react-native-reanimated react-native-gesture-handler @shopify/react-native-skia

Reanimated also needs its Babel plugin, which the documentation says to add to the `plugins` list in your `babel.config.js` config file as `"react-native-reanimated/plugin"`. That step is easy to skip and hard to debug, since without it the animations silently do not work.

Then the library itself:

shell
yarn add victory-native

The order matters, because Reanimated's Babel plugin has to be the last plugin in the list, and installing the libraries in the documented order makes it easier to get right.

The monorepo shape, and what the workspaces hold

The repository root is a private monorepo package named `victory-native-xl-monorepo` with two workspaces, `lib` and `example`.

The `lib` workspace is the library that gets published. The `example` workspace is a full React Native application that consumes it, with an `app/` directory, `components/`, `data/`, `hooks/` and `consts/` directories, and its own `babel.config.js`, `app.json` and `tsconfig.json`. There are also two entry points in the example, `index.js` and `index.web.tsx`, which is a hint that the same chart code is expected to run on the web as well as on a device.

The build script shows how the package is assembled. It copies the root README into the library workspace and then runs the workspace build:

shell
shx cp README.md ./lib/README.md && yarn workspace victory-native build

That copy step is a small detail with a real purpose, since the published package needs its own README and copying is the simplest way to keep the two in step.

The development scripts run the library and the example together. `yarn dev` uses `concurrently` to run `dev:lib` and `dev:example` side by side, with the names lib and example and colours blue and green in the output, and there is a `dev:ios` variant that passes `--ios` through to the example. The docs site is a third project, a `website/` directory with its own install and build scripts, and `docs:install` uses `yarn install --frozen-lockfile` in that directory, which is the right flag for a site that is not part of the workspace.

The repository also carries a `.changeset/` directory and a `changeset` script, so releases are cut with changesets, and the `version` script runs `yarn changeset version` followed by an install with `--no-immutable`, which is how the lockfile gets updated after a version bump.

Checking the code is one command, not three

The scripts are arranged so that the same checks run in the same places whether you are a contributor or a release automation.

The `check:code` script is the interesting one:

shell
yarn lint && yarn typecheck && yarn test

Three checks in sequence, and the order matters for diagnosis. Lint runs first through `eslint --ext .js,.ts,.tsx example lib`, covering both the library and the example application, which is the point of having the example in the same repository. Typecheck runs `tsc --noEmit` next, so type errors are reported without emitting anything. The test script then delegates to the library workspace with `yarn workspace victory-native test`, and a `test:watch` variant is there for the loop while you work.

The lint configuration is thorough, which is normal for a library that other people depend on. The dev dependencies include `eslint-config-prettier` and `eslint-plugin-prettier` so that formatting and linting do not argue with each other, `eslint-plugin-react`, `eslint-plugin-react-hooks` and `eslint-plugin-import` for the React specific rules, and `eslint-import-resolver-typescript` so that import resolution understands TypeScript paths rather than only JavaScript ones. There is a `.prettierrc` for the formatting rules and an `.eslintignore` for the exclusions.

The toolchain versions are pinned aggressively, which is a deliberate choice for a library with a large peer dependency surface. TypeScript is at `^5.6.3`, the ESLint stack is at 8.x, the Node engine requirement is `>=24.0.0`, and the package manager is declared as `[email protected]` with a `.yarnrc.yml` and a `yarn.lock` in the repository. The `.node-version` file at the root gives the expected Node version as well.

Maintenance status, versioning and the XL naming

The repository states its maintenance status explicitly, which is more than most do. Nearform is actively working on this project, and the project expects to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome. There is a maintenance badge in the README that says active.

The dates line up with that. The last push was on 2026-08-31, the repository is not archived, and the recent releases are 41.26.0 on 2026-06-09, 42.0.0 on 2026-08-25 and 42.0.1 on 2026-08-31.

The version numbers deserve a note. 41.26.0 is a minor bump in the 41 line, and then the project moves to 42.0.0 and a patch. A project at 42 with a change from 41 to 42 is one that ships breaking changes when the major version moves, which for a charting library is the moment to read the changelog rather than to let npm do it for you.

The naming is the other thing to be clear about. The repository is `FormidableLabs/victory-native-xl`, the root package is `victory-native-xl-monorepo`, and the package published to npm is `victory-native`. The README also links the maintenance status section by an anchor that reads maintenance-status on the victory-native-xl repository. So the XL name is the repository and the product family, while `victory-native` is the installable package name. That is an unusual amount of naming in one project, and it is exactly the kind of thing that costs you twenty minutes if you go looking for the wrong repository.

The homepage for the project is the Nearform open source page for Victory Native, and the API documentation is at formidable.com under open source, victory-native.

What you take on when you pick this over a canvas or SVG approach

The choice of this library is a choice about how charts are drawn on a phone, and it is worth being concrete about the trade.

The alternative most React Native charting code uses is SVG, rendered by a library such as react-native-svg. SVG is a scene description, and React Native has to translate that description into native view operations, one view or a few per path. A chart with a few hundred data points becomes a few hundred views, which is a lot of native objects for the platform to manage and a lot of memory.

Skia does not work that way. It is an immediate mode GPU renderer with a single native surface, and a chart becomes drawing commands rather than a tree of view objects. That is where the performance claim comes from, and it is also where the complexity comes from, because you now depend on a native graphics library and on the versions of Skia, Reanimated and Gesture Handler lining up with your React Native version.

The second trade is about animation. Reanimated runs work on the UI thread, so a gesture-driven zoom or pan does not wait for the JavaScript bridge. That is the difference between a chart that tracks a finger and one that stutters behind it, and it is the reason the library asks for the Reanimated Babel plugin to be configured properly rather than being an optional extra.

The third trade is D3. Using D3 means the scales, shapes and layout conventions are the ones the rest of the chart ecosystem uses, so a chart you design here can be reproduced in a web chart with the same maths. The cost is that you are working with D3 concepts, and the library expects you to be comfortable with them.

The practical cost to weigh is the peer dependency surface. Three native libraries, a Babel plugin, a Node 24 requirement, yarn 4, and a React Native version that has to agree with all of it. None of that is unusual for a mobile project in 2026, but it is a real amount of moving parts to add to an application that might only need a bar chart.

Editorial conclusion

Victory Native XL fits a React Native application whose charts need to stay smooth under touch, because the rendering goes through Skia and the animation work is off the JavaScript thread in Reanimated, which is the whole point of the library. Skip it if your charts are static or if your app is on the web, since the same project ships a separate web entry point and there is a `index.web.tsx` file in the example for that case. Before you adopt it, check that your React Native version plays well with Skia and Reanimated, because the library installs none of them itself, and note the release shape, with versions 41.26.0 in June 2026 and 42.0.0 and 42.0.1 in August 2026, so the major version is moving and the README also mentions a XL variant alongside the package published as `victory-native`.

Frequently asked questions

How do I install Victory Native XL?

Install the peer dependencies first with `yarn add react-native-reanimated react-native-gesture-handler @shopify/react-native-skia`, add `"react-native-reanimated/plugin"` to the plugins list in `babel.config.js`, then run `yarn add victory-native`.

What rendering technologies does Victory Native XL use?

D3 for the chart maths, Skia for rendering the primitives and Reanimated with Gesture Handler for the interaction and animation work, which runs on the UI thread rather than the JavaScript thread.

Is Victory Native XL actively maintained?

The repository states that Nearform is actively working on the project and expects to continue for the foreseeable future, with bug reports, feature requests and pull requests welcome. The last push was on 2026-08-31.

How are releases managed in the Victory Native XL repository?

With changesets. The repository has a `.changeset/` directory, a `changeset` script, and a `version` script that runs `yarn changeset version` followed by an install to update the lockfile.

What is the difference between the repository name and the npm package name?

The repository and monorepo root package are named `victory-native-xl` and `victory-native-xl-monorepo`, while the package published to npm and installed by consumers is `victory-native`.

Official sources

  1. FormidableLabs/victory-native-xl on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/formidablelabs-victory-native-xl.svg)](https://hysenlabs.com/projects/formidablelabs-victory-native-xl)

Was this useful?