Choosing a Charting Library for React Native
Before we could think about reusable chart components, tooltips, touch targets, or interaction architecture, we had a more fundamental decision to make:
Which charting library should we build on?
The React Native ecosystem has several options.
At a high level, our requirements weren't simply:
Can this library render a line chart?
Most charting libraries can.
The more important questions were:
Can we customize the chart deeply?
Can we build touch-first interactions?
Can we control gestures?
Can we create custom visual elements?
Can it handle animations smoothly?
Can we reuse the underlying chart primitives?
Can we integrate it into our own design system?
Can we build abstractions around it instead of
being forced into the library's abstractions?
That distinction became important.
We weren't looking for a library that could give us a good-looking chart in five minutes.
We were looking for a foundation for a charting system.
Why We Chose Victory Native
After evaluating the available approaches, we decided to use Victory Native as the foundation for our charts.
The current generation of Victory Native is designed specifically around high-performance and customizable React Native charts. Under the hood, it combines technologies including D3, Shopify Skia, React Native Reanimated, and React Native Gesture Handler.
That architecture aligned well with what we needed.
Our charts weren't intended to be static dashboard images.
They needed to become interactive surfaces.
We wanted users to be able to:
Touch
↓
Select
↓
Drag
↓
Inspect
↓
Compare
↓
Understand
without the visualization feeling like a desktop chart squeezed onto a phone.
Performance Was an Important Factor
Touch interaction changes the performance requirements of a chart.
A static chart might render once and remain unchanged.
An interactive chart can update continuously while a user moves their finger.
Finger movement
↓
Gesture update
↓
Chart state changes
↓
Selected coordinate changes
↓
Indicator moves
↓
Tooltip changes
↓
Frame renders
That can happen many times per second.
Victory Native's current architecture uses Skia for graphics and Reanimated for animation, which made it attractive for the kind of fluid interaction we wanted to build.
Rather than relying entirely on normal React rendering for every visual update, these technologies give us tools designed for high-performance graphical interfaces.
We Needed More Than Prebuilt Charts
Some charting libraries optimize for this experience:
<LineChart
data={data}
width={350}
height={220}
/>
And you're done.
That is excellent when your requirements closely match the library's defaults.
Our requirements were different.
We needed to think about:
Custom indicators
Custom labels
Touch tracking
Selected states
Specialized tooltips
Dynamic ranges
Custom axis behavior
Product-specific visual language
Reusable interaction patterns
So the ability to compose and customize the visualization mattered more than having the shortest possible implementation.
We Wanted Control Over Gestures
A mobile chart isn't just something the user looks at.
It is something they interact with.
For example:
User touches here
↓
Mon Tue Wed Thu Fri
●─────●──────●──────●─────●
↑
nearest point
Then:
Finger moves right
↓
Gesture updates
↓
Selection moves
↓
Tooltip updates
Victory Native's integration with React Native Gesture Handler and Reanimated makes this type of interaction much more natural to build than trying to reproduce desktop hover behavior.
This was particularly important for us because we didn't want interactions to be an afterthought.
They were part of the visualization architecture from the beginning.
The Trade-Off: Victory Native Has a Steeper Learning Curve
Choosing Victory Native did not mean everything became easy.
In fact, one of the biggest challenges we encountered was learning how to use the library beyond basic examples.
This is an important part of the story because library selection isn't simply about comparing feature lists.
Developer experience matters too.
And this was one area where Victory Native required more engineering effort from us.
The Documentation Challenge
For basic chart rendering, getting started is relatively straightforward.
The challenge appears when your requirements become more specific.
You might start with:
<CartesianChart
data={data}
xKey="date"
yKeys={["value"]}
>
{/* chart */}
</CartesianChart>
Then product requirements arrive.
You need:
A custom tooltip.
A selected-point indicator.
Different behavior at chart boundaries.
Custom axis labels.
Dynamic domains.
Touch tracking.
A special marker.
Multiple chart states.
Custom gesture behavior.
At that point, understanding the right implementation often requires considerably more than copying a documentation example.
Why This Happens
Victory Native exposes relatively powerful lower-level concepts.
That flexibility is valuable.
But flexibility creates a larger API surface to understand.
When building more advanced interactions, developers may need to understand several technologies at once:
Victory Native
│
├── Chart primitives
│
├── D3 concepts
│
├── Skia
│
├── Reanimated
│
└── Gesture Handler
So debugging a chart issue isn't always:
Which Victory prop am I missing?
Sometimes it becomes:
Is this a chart-state problem?
or:
Is this coordinate transformation wrong?
or:
Is this Skia rendering behavior?
or:
Is the gesture state updating incorrectly?
or:
Is this value on the UI thread?
That makes the learning curve noticeably steeper.
Documentation Is Especially Important for Edge Cases
Most libraries document the happy path well.
Production applications live in the edge cases.
For example:
What happens when there is one data point?
What happens when every value is identical?
What happens when values are null?
What happens when the user drags outside the chart?
How do we position a tooltip at the first point?
What happens at the right edge?
How do multiple datasets interact?
How do we customize the selected state?
These are the questions that usually take more engineering time than rendering the chart itself.
For some of these cases, we found ourselves needing to go beyond the primary documentation and understand examples, library behavior, underlying APIs, and implementation details.

Why We Still Chose Victory Native
This creates an interesting trade-off.
Victory Native
Flexibility Simplicity
▲ ▲
│ │
High Lower
A simpler library might have allowed us to render the first version of a chart faster.
Victory Native gave us something we considered more valuable:
room to build the interaction system we actually wanted.
Our decision therefore wasn't:
Which library is easiest to use?
It was:
Which library gives us the right foundation when our charts become more complex?
That is a very different evaluation criterion.
Victory Native vs Other React Native Charting Libraries
There is no universally "best" React Native charting library.
Different libraries optimize for different goals.
Some focus on:
Quick setupOthers focus on:
A wide variety of prebuilt chart typesOthers focus on:
Customization optionsAnd others focus on:
Performance and interactivityFor our needs, we assessed the trade-offs rather than just tallying features.
Victory Native
Victory Native was the strongest fit when we considered customization and interaction as first-class requirements.
Its current architecture is built around:
Victory Native
│
├── D3
├── Skia
├── Reanimated
└── Gesture Handler
That gives developers significant control over rendering, animation, chart state and gesture-driven interaction.
Strengths
High customization
Performance-oriented rendering
Powerful touch interaction
Skia-based graphics
Reanimated integration
Gesture Handler integration
Composable architecture
Suitable for sophisticated charts
Trade-offs
Steeper learning curve
More concepts to understand
Advanced use cases can require deeper exploration
More setup than some simpler libraries
Documentation can feel limited for complex edge cases
Victory Native therefore made the most sense for us when the requirement was:
Build a visualization system.
rather than:
Put a chart on this screen.
React Native Gifted Charts
Another popular option is React Native Gifted Charts.
It provides a broad collection of chart types including:
line,
area,
bar,
stacked bar,
pie,
donut,
radar,
bubble,
scatter,
and population-pyramid visualizations.
It also provides features such as animations, gradients, clickable charts, scrolling and combined visualizations.
For many applications, this can make the developer experience very attractive.
You can get useful visualizations running with relatively little code.
Conceptually:
<LineChart data={data} />and then configure behavior through props.
Strengths
Quick to get started
Many chart types
Large configuration surface
Animations available out of the box
Detailed examples
Good for conventional product dashboards
Trade-offs
For highly specialized interaction systems, a prop-driven abstraction can eventually become something you need to work around.
That doesn't make the library worse.
It simply means it is optimized for a different balance between:
Convenience
↕
Low-level control
Gifted Charts is particularly compelling when a product needs a broad collection of familiar chart types with relatively quick implementation.
Its project also emphasizes detailed documentation and examples, which can make discovering features easier.
React Native Chart Kit
React Native Chart Kit is another established option.
Its appeal is easy to understand.
For conventional charts, the API can be straightforward.
<LineChart
data={data}
width={width}
height={220}
/>
It supports common visualization types such as:
Line
Area
Bar
Pie
Donut
Progress
Contribution heatmaps
That makes it useful for applications where charts primarily serve as visual summaries.
Strengths
Straightforward API
Quick setup
Common chart types
Good for conventional dashboards
Lower initial learning curve
Trade-offs
Our requirements extended beyond rendering conventional charts.
We cared heavily about:
Gesture-driven interaction
Custom rendering
Custom selection behavior
Reusable graphical primitives
Fine-grained visual control
For those requirements, Victory Native's lower-level architecture was a better fit.
A Simplified Comparison
Our evaluation can be summarized conceptually like this:
Requirement | Victory Native | Gifted Charts | Chart Kit |
Quick initial setup | Moderate | Strong | Strong |
Standard chart types | Strong | Very strong | Strong |
Deep customization | Very strong | Strong | Moderate |
Gesture-driven interaction | Very strong | Strong | Moderate |
High-performance custom rendering | Very strong | Good | Good |
Advanced animation architecture | Very strong | Strong | Moderate |
Documentation discoverability | Moderate | Strong | Strong |
Learning curve | Higher | Moderate | Lower |
Best fit | Custom interactive visualization systems | Feature-rich product charts | Straightforward charts |
This isn't meant to declare a winner.
It demonstrates why library selection depends on what you're building.
The Decision Changes With the Product
If we were building a simple admin dashboard containing:
Revenue bar chart
User-growth line chart
Traffic pie chart
our decision might have been different.
A library optimized for rapid implementation could be the better engineering choice.
But our visualization requirements involved more interaction.
We needed users to be able to explore data naturally on a touchscreen.
That meant our priorities looked more like:
Customization
↓
Touch interaction
↓
Performance
↓
Reusable architecture
↓
Visual control
↓
Ease of initial implementation
Notice where ease of implementation appears.
It matters.
It just wasn't our highest priority.
Don't Select a Charting Library From the Demo Page
One lesson we learned is that evaluating chart libraries by looking at their gallery can be misleading.
Most libraries can produce:
Line chart ✓
Bar chart ✓
Pie chart ✓
Area chart ✓
That doesn't tell you whether the library fits your product.
A better evaluation is to take your hardest expected chart and prototype it.
For us, that means testing things such as:
Track the user's finger
Select the nearest data point
Customize the selected indicator
Render our own tooltip
Handle chart boundaries
Integrate the product's design language
Animate interaction smoothly
Abstract this behind reusable components
If the library handles the hardest visualization well, the simple charts usually follow.
The reverse is not necessarily true.
Our Decision Framework
The decision ultimately looked something like this:
Requirements
│
▼
┌─────────────────────┐
│ Standard charts only?│
└──────────┬──────────┘
│
Yes ───┴─── No
│ │
▼ ▼
Simpler library Need custom
may be enough interaction?
│
Yes
│
▼
Need performance +
graphical control?
│
Yes
│
▼
Victory Native
Victory Native wasn't selected because it required the least code.
It was selected because its architecture aligned most closely with the interaction model we wanted to build.
The Important Lesson: Wrap the Library Anyway
Even after deciding on Victory Native, we made another important architectural decision:
The rest of our application shouldn't know that decision was made.
Screens should not be filled with:
<CartesianChart ...>
...
</CartesianChart>
They should consume something closer to:
<TrendChart
data={data}
xKey="date"
yKey="value"
/>
That gives us:
Product Screens
│
▼
Our Charting API
│
▼
Victory Native
│
├── Skia
├── Reanimated
└── Gesture Handler
This boundary matters even more because charting libraries evolve.
If Victory Native changes significantly in the future — or if our requirements eventually require a different rendering approach — we want the migration to happen inside the charting toolkit rather than across every screen in the application.
That leads to one of the broader engineering lessons from this project:
Choose libraries based on their capabilities, but design your application so that the library never becomes your architecture.
Victory Native gave us the flexibility and performance characteristics we needed.
Our reusable charting layer gives us control over what happens next.
Thanks, it's helpful.