Victory Native vs React Native Chart Kit vs ECharts: Mobile Charts 2026
TL;DR
Data visualization on mobile is harder than web — no SVG in the native renderer, performance constraints on 60fps animations, and touch interactions that feel native. Victory Native XL (the rewritten version using React Native Skia) renders charts directly on the GPU via Skia canvas — smooth 60fps animations, declarative API, and full TypeScript types; it's the modern choice for performant React Native charts. React Native Chart Kit is the pragmatic option — uses SVG via react-native-svg, covers the most common chart types with minimal configuration, and has been the community default for years despite limited customization. Apache ECharts for React Native brings the full power of the ECharts ecosystem — 20+ chart types, rich interactions, WebView-based rendering; best when you need advanced chart types or are already using ECharts on web. For performant, customizable charts with Skia: Victory Native XL. For quick standard charts with SVG: React Native Chart Kit. For advanced chart types ported from a web ECharts implementation: Apache ECharts.
Key Takeaways
- Victory Native XL uses React Native Skia — GPU-accelerated canvas, not SVG
- React Native Chart Kit uses SVG — via
react-native-svg, more compatible - Apache ECharts uses WebView — full web ECharts in a WebView; most chart variety
- Victory Native XL supports Reanimated — gesture-driven interactions (pan, zoom)
- React Native Chart Kit: no live updates — static charts, no built-in animation
- ECharts React Native: 20+ chart types — map, sankey, sunburst, candlestick, etc.
- Victory Native XL is a complete rewrite — not backward-compatible with Victory Native v36
Chart Type Coverage
Bar chart → All three ✅
Line chart → All three ✅
Pie / Donut → All three ✅
Area chart → Victory Native XL, RN Chart Kit ✅
Scatter plot → Victory Native XL, ECharts ✅
Candlestick → ECharts ✅
Heatmap → ECharts ✅
Sankey diagram → ECharts ✅
Radar chart → ECharts, Victory Native XL ✅
Map / Geo → ECharts ✅
Progress ring → Victory Native XL ✅
Sparkline → Victory Native XL ✅
Victory Native XL: Skia-Powered Charts
Victory Native XL (v40+) is a complete rewrite of Victory Native using React Native Skia for direct GPU canvas rendering. It supports gesture-driven interactions via react-native-gesture-handler and react-native-reanimated.
Installation
npm install victory-native
# Required peer dependencies:
npm install react-native-skia react-native-reanimated react-native-gesture-handler
npx pod-install # iOS
Line Chart
import { CartesianChart, Line, useChartPressState } from "victory-native";
import { Circle, useFont } from "@shopify/react-native-skia";
import { useSharedValue } from "react-native-reanimated";
const data = [
{ day: "Mon", revenue: 1200 },
{ day: "Tue", revenue: 1800 },
{ day: "Wed", revenue: 1500 },
{ day: "Thu", revenue: 2100 },
{ day: "Fri", revenue: 2400 },
{ day: "Sat", revenue: 1900 },
{ day: "Sun", revenue: 800 },
];
function RevenueChart() {
const font = useFont(require("../assets/fonts/Inter-Regular.ttf"), 12);
const { state, isActive } = useChartPressState({ x: "day", y: { revenue: 0 } });
return (
<CartesianChart
data={data}
xKey="day"
yKeys={["revenue"]}
axisOptions={{
font,
formatYLabel: (value) => `$${value.toLocaleString()}`,
labelColor: "#666",
lineColor: "#e0e0e0",
}}
chartPressState={state}
>
{({ points }) => (
<>
<Line
points={points.revenue}
color="#3b82f6"
strokeWidth={2}
animate={{ type: "spring" }}
/>
{isActive && (
<Circle
cx={state.x.position}
cy={state.y.revenue.position}
r={8}
color="#3b82f6"
/>
)}
</>
)}
</CartesianChart>
);
}
Bar Chart
import { CartesianChart, Bar } from "victory-native";
function SalesBarChart() {
return (
<CartesianChart
data={data}
xKey="day"
yKeys={["revenue"]}
domainPadding={{ left: 20, right: 20, top: 20 }}
>
{({ points, chartBounds }) => (
<Bar
chartBounds={chartBounds}
points={points.revenue}
color="#3b82f6"
roundedCorners={{ topLeft: 4, topRight: 4 }}
animate={{ type: "spring", duration: 300 }}
/>
)}
</CartesianChart>
);
}
Pie / Donut Chart
import { PieChart } from "victory-native";
const pieData = [
{ label: "Direct", value: 40, color: "#3b82f6" },
{ label: "Organic", value: 25, color: "#10b981" },
{ label: "Paid", value: 20, color: "#f59e0b" },
{ label: "Referral", value: 15, color: "#ef4444" },
];
function TrafficSourceChart() {
return (
<PieChart
data={pieData}
labelKey="label"
valueKey="value"
colorKey="color"
outerRadius="80%"
innerRadius="50%" // Donut chart
animate={{ type: "spring" }}
/>
);
}
Interactive Tooltip
import { CartesianChart, Line } from "victory-native";
import { useChartPressState } from "victory-native";
import Animated, { useAnimatedStyle, useDerivedValue } from "react-native-reanimated";
function InteractiveChart() {
const { state, isActive } = useChartPressState({ x: "day", y: { revenue: 0 } });
const tooltipLabel = useDerivedValue(() =>
isActive.value
? `${state.x.value.value}: $${state.y.revenue.value.value.toFixed(0)}`
: ""
);
return (
<View>
{/* Tooltip */}
<Animated.Text style={tooltipStyle}>{tooltipLabel}</Animated.Text>
<CartesianChart
data={data}
xKey="day"
yKeys={["revenue"]}
chartPressState={state}
style={{ height: 250 }}
>
{({ points }) => <Line points={points.revenue} color="#3b82f6" strokeWidth={2} />}
</CartesianChart>
</View>
);
}
React Native Chart Kit: SVG Charts
React Native Chart Kit is the long-standing community choice for standard charts via SVG — straightforward API, covers most use cases, with react-native-svg under the hood.
Installation
npm install react-native-chart-kit react-native-svg
npx pod-install # iOS
Line Chart
import { LineChart } from "react-native-chart-kit";
import { Dimensions } from "react-native";
const screenWidth = Dimensions.get("window").width;
const chartConfig = {
backgroundColor: "#ffffff",
backgroundGradientFrom: "#ffffff",
backgroundGradientTo: "#ffffff",
decimalPlaces: 0,
color: (opacity = 1) => `rgba(59, 130, 246, ${opacity})`,
labelColor: (opacity = 1) => `rgba(100, 116, 139, ${opacity})`,
strokeWidth: 2,
propsForDots: {
r: "4",
strokeWidth: "2",
stroke: "#3b82f6",
},
};
function RevenueLineChart() {
return (
<LineChart
data={{
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
datasets: [
{
data: [1200, 1800, 1500, 2100, 2400, 1900, 800],
color: (opacity = 1) => `rgba(59, 130, 246, ${opacity})`,
strokeWidth: 2,
},
],
}}
width={screenWidth - 32}
height={220}
chartConfig={chartConfig}
bezier // Smooth curves
style={{ borderRadius: 12 }}
formatYLabel={(value) => `$${parseInt(value).toLocaleString()}`}
onDataPointClick={({ value, index }) => {
console.log(`Tapped: ${value} at index ${index}`);
}}
/>
);
}
Bar and Pie Charts
import { BarChart, PieChart, ProgressChart } from "react-native-chart-kit";
function SalesBarChart() {
return (
<BarChart
data={{
labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
datasets: [{ data: [1200, 1800, 1500, 2100, 2400] }],
}}
width={screenWidth - 32}
height={220}
chartConfig={chartConfig}
yAxisLabel="$"
yAxisSuffix=""
showValuesOnTopOfBars
/>
);
}
function TrafficPieChart() {
return (
<PieChart
data={[
{ name: "Direct", population: 40, color: "#3b82f6", legendFontColor: "#666", legendFontSize: 12 },
{ name: "Organic", population: 25, color: "#10b981", legendFontColor: "#666", legendFontSize: 12 },
{ name: "Paid", population: 20, color: "#f59e0b", legendFontColor: "#666", legendFontSize: 12 },
]}
width={screenWidth - 32}
height={200}
chartConfig={chartConfig}
accessor="population"
backgroundColor="transparent"
paddingLeft="10"
/>
);
}
Apache ECharts for React Native: WebView Charts
ECharts for React Native wraps Apache ECharts in a WebView — full access to ECharts' 20+ chart types, themes, and interactions.
Installation
npm install @wuba/react-native-echarts react-native-webview react-native-svg
npx pod-install # iOS
Line Chart with ECharts
import { useRef, useState, useEffect } from "react";
import { SkiaChart, SVGRenderer } from "@wuba/react-native-echarts";
import * as echarts from "echarts/core";
import { LineChart } from "echarts/charts";
import { GridComponent, TooltipComponent, LegendComponent } from "echarts/components";
// Register required components
echarts.use([SVGRenderer, LineChart, GridComponent, TooltipComponent, LegendComponent]);
function EChartsLineChart() {
const chartRef = useRef(null);
useEffect(() => {
const chart = echarts.init(chartRef.current, "light", {
renderer: "svg",
width: 350,
height: 250,
});
chart.setOption({
tooltip: {
trigger: "axis",
formatter: (params: any[]) => {
return `${params[0].axisValue}: $${params[0].value.toLocaleString()}`;
},
},
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {
type: "value",
axisLabel: { formatter: (val: number) => `$${val.toLocaleString()}` },
},
series: [
{
data: [1200, 1800, 1500, 2100, 2400, 1900, 800],
type: "line",
smooth: true,
lineStyle: { width: 2, color: "#3b82f6" },
areaStyle: { color: "rgba(59, 130, 246, 0.1)" },
},
],
});
return () => chart.dispose();
}, []);
return <SkiaChart ref={chartRef} />;
}
Advanced: Candlestick Chart (Not available in other libraries)
import { CandlestickChart } from "echarts/charts";
echarts.use([CandlestickChart]);
function StockChart() {
const chartRef = useRef(null);
useEffect(() => {
const chart = echarts.init(chartRef.current, "light", {
renderer: "svg",
width: 350,
height: 300,
});
// [open, close, low, high]
const stockData = [
[150, 155, 148, 157],
[155, 152, 149, 158],
[152, 160, 151, 162],
[160, 158, 155, 163],
[158, 165, 156, 167],
];
chart.setOption({
xAxis: {
data: ["Mon", "Tue", "Wed", "Thu", "Fri"],
},
yAxis: { scale: true },
series: [
{
type: "candlestick",
data: stockData,
itemStyle: {
color: "#10b981", // Bullish
color0: "#ef4444", // Bearish
borderColor: "#10b981",
borderColor0: "#ef4444",
},
},
],
});
return () => chart.dispose();
}, []);
return <SkiaChart ref={chartRef} />;
}
Feature Comparison
| Feature | Victory Native XL | RN Chart Kit | ECharts RN |
|---|---|---|---|
| Renderer | Skia (GPU canvas) | SVG | WebView/SVG |
| Performance | ✅ Excellent | ✅ Good | ⚠️ WebView overhead |
| Chart types | 8 | 8 | 20+ |
| Gesture/zoom | ✅ Reanimated | ❌ | ✅ |
| Animations | ✅ Spring | Limited | ✅ Rich |
| Candlestick | ❌ | ❌ | ✅ |
| Heatmap | ❌ | ❌ | ✅ |
| Sankey/Flow | ❌ | ❌ | ✅ |
| TypeScript | ✅ Excellent | ✅ Good | ✅ Good |
| Bundle size | Medium | Small | Large (ECharts) |
| Custom drawing | ✅ Skia canvas | ❌ | ❌ |
| GitHub stars | 1.6k | 2.5k | 4k |
When to Use Each
Choose Victory Native XL if:
- Performance is critical — 60fps animations with gesture-driven interactions (pan, zoom, scrub)
- Skia canvas gives access to custom drawing alongside charts
- Standard chart types (line, bar, area, pie, scatter) cover your needs
- TypeScript-first, modern React Native stack (Skia + Reanimated + Gesture Handler)
Choose React Native Chart Kit if:
- Quick standard charts without performance requirements
- Simpler setup — just
react-native-svgas a dependency - Community-familiar API that's been stable for years
- No need for gesture interactions or animation beyond basic entrance animations
Choose ECharts React Native if:
- Advanced chart types not available in other libraries (candlestick, sankey, heatmap, geo maps)
- You're porting a web ECharts implementation to React Native
- Rich built-in interactions (click to filter, drill-down, zoom) matter
- WebView overhead is acceptable for your use case
Performance Benchmarks and Frame Rate Considerations
The rendering approach of each library directly determines its viability for data-dense, frequently-updating dashboards. Victory Native XL's Skia canvas renders on the GPU thread through the Fabric architecture, which means animation and data updates do not block the JavaScript thread — charts animate at 60fps even when your JS thread is processing network responses. React Native Chart Kit renders via SVG, which is composited by the native rendering pipeline and performs well for static or infrequently-updated charts, but large SVG DOM trees with hundreds of data points can cause noticeable redraws. The ECharts WebView approach introduces the most overhead: every chart.setOption() call serializes data across the WebView bridge, which adds latency proportional to data size. For dashboards that update in real-time (stock tickers, live sensor readings), Victory Native XL is the only option with the performance headroom to update at 60fps. For charts that update every few seconds or only on user action, all three libraries perform acceptably.
TypeScript Integration and Type-Safe Chart Data
Victory Native XL ships with TypeScript definitions that enforce the relationship between your data array's key names and the xKey/yKeys props — passing a key that doesn't exist in your data array is a TypeScript error at development time. The useChartPressState hook is also generic over your data shape, so state.x.value and state.y.revenue.value are properly typed based on the keys you declare. React Native Chart Kit's TypeScript support covers the component props but does not enforce that your datasets array contains numeric values — you can pass strings and only see failures at runtime. ECharts for React Native operates through the imperative chart.setOption() API, which accepts an untyped options object; there are community @types/echarts definitions but they are not enforced at the series data level. For codebases where TypeScript strictness matters throughout, Victory Native XL's type safety at the data-to-chart binding layer is a meaningful advantage.
New Architecture Requirements and Expo Compatibility
Victory Native XL requires the New Architecture (Fabric renderer + JSI) because React Native Skia uses JSI for direct communication between JavaScript and the native Skia rendering engine. This means Victory Native XL is not compatible with Expo Go (which runs the Old Architecture) — you must use a development build via npx expo run:ios or eas build. For teams still on the Old Architecture in production, this is a blocking constraint. React Native Chart Kit and ECharts for React Native both work with the Old Architecture and are compatible with Expo Go for development — this significantly lowers the barrier to adding charts to a managed Expo workflow. If your team is not yet on the New Architecture and the timeline to migrate is uncertain, React Native Chart Kit is the pragmatic choice today. If you are greenfield or have already migrated, Victory Native XL's Skia foundation positions it well for the React Native ecosystem's long-term direction.
Migration Path from React Native Chart Kit to Victory Native XL
Teams with an existing React Native Chart Kit codebase can migrate incrementally since the libraries are not coupled — you can render Victory Native XL charts in new screens while leaving existing Chart Kit charts in place. The API surface is substantially different: Chart Kit uses a data object with labels and datasets arrays, while Victory Native XL uses a flat array of typed objects with explicit key mappings. The mental model shift is from "dataset as parallel arrays" to "dataset as records," which aligns better with how data typically arrives from APIs. The setup cost of adding Skia, Reanimated, and Gesture Handler as peer dependencies is the primary friction point — these are significant native modules that require pod installation and potentially a Xcode clean build. Budget a half-day for initial setup and a few days to migrate complex chart screens with custom interactions. The payoff is charts that feel genuinely native rather than web-technology-embedded.
Methodology
Data sourced from official Victory Native documentation (commerce.nearform.com/open-source/victory-native), React Native Chart Kit GitHub repository, Apache ECharts for React Native documentation (wuba.github.io/react-native-echarts), npm download statistics as of February 2026, GitHub star counts as of February 2026, and community discussions from the React Native Discord and r/reactnative.
Related: FlashList vs FlatList vs LegendList for the list rendering performance that often houses chart data, or React Native Reanimated vs Moti vs Skia for the animation libraries that Victory Native XL is built on.
See also: React vs Vue and React vs Svelte