Data visualization plays a key role in modern mobile apps — from analytics dashboards to performance summaries. Well-designed graphs make complex data easy to understand at a glance. However, building responsive and high-performance graphs in React Native can be challenging.
In this blog, we will walk through how we approached building responsive and performant graphs in React Native — the challenges we faced and the strategies we adopted.
The Challenges We Faced with Graph Implementations
When working with popular graph libraries in React Native, several practical issues often arise, particularly around responsiveness, alignment, and performance. Here are a few of the most common challenges we encountered:
1. Y-Axis Scrolling Issue
When making the graph horizontally scrollable (for large datasets), the Y-axis scrolled along with the graph content, making it difficult to keep the labels visible while navigating through the data.
2. Axis Alignment Issues
To fix the above issue, we attempted to create two separate graphs — one for the Y-axis and another for the main scrollable X-axis. While this approach initially worked, it introduced alignment inconsistencies between the two graphs, especially when the chart data or layout was updated dynamically.
3. Performance and Crashes on iOS
For large datasets, especially in bar charts or grouped bar charts, the app would occasionally crash on iOS. The crash logs pointed to Metal texture creation failures, meaning the graphics rendering system could not allocate textures large enough for the entire dataset.
What We Require from Our Charting Solution
After evaluating multiple libraries and approaches, it became clear that our ideal solution had to:
- Handle large datasets gracefully without crashes.
- Support independent Y-axis rendering for better user readability.
- Offer responsiveness across screen sizes.
- Be lightweight and performant, minimizing bundle size and build times.
- Stay easy to integrate.
The Solution
After exploring several options, we found that combining a lightweight, flexible charting library like react-native-gifted-charts with custom dynamic logic gave us the right balance between performance, maintainability, and control.
Some of the features that stood out about the library were:
- Built-in support for scrollable charts with fixed axes
- Smooth animations and transitions
- Highly customizable props for colors, labels, gradients, and legends
- Optimized rendering that prevents crashes even with large datasets
- Lightweight and efficient, which reduced the app's bundle size, improved runtime performance, and significantly shortened the time required to create production builds
1. Installation
To get started, install the necessary dependencies that will enable chart rendering and responsive scaling in your React Native project:
-
react-native-gifted-charts(the charting library) -
react-native-svg(for rendering graphics) -
react-native-linear-gradient(orexpo-linear-gradientif in an Expo project) -
react-native-responsive-screenfor dynamic sizing across devices (recommended)
Using React Native CLI:
npm install react-native-gifted-charts react-native-svg react-native-linear-gradient react-native-responsive-screen
Using Expo (recommended):
npx expo install react-native-gifted-charts react-native-svg expo-linear-gradient react-native-responsive-screen
Note: Make sure these dependencies are linked (in non-Expo projects) so the native modules work correctly. Autolinking should handle it in recent React Native versions.
2. The Final Optimized Implementation
Let's walk through the final optimized setup step-by-step, starting from the base configuration and moving into the dynamic enhancements we added to make the graphs stable, scalable, and production-ready.
Setting Up the Base Chart
The react-native-gifted-charts library provides a solid and efficient starting point for creating bar graphs. It handles much of the heavy lifting for graph rendering, animations, and performance optimizations internally.
Here's a basic setup to render a simple bar chart:
import { BarChart } from 'react-native-gifted-charts';
import { widthPercentageToDP as wp } from 'react-native-responsive-screen';
const barData = [
{ value: 250, label: 'Jan' },
{ value: 500, label: 'Feb' },
{ value: 745, label: 'Mar' },
{ value: 320, label: 'Apr' },
{ value: 600, label: 'May' },
];
const SimpleBarChart = () => {
return (
<BarChart
data={barData}
barWidth={wp('5.5%')}
height={wp('38%')}
width={wp('90%')}
spacing={65}
noOfSections={5}
maxValue={1000}
stepValue={200}
frontColor="#4ABFF4"
/>
);
};
This base setup alone helps resolve two major pain points:
-
App crashes on iOS for large datasets:
react-native-gifted-chartshandles large datasets smoothly without triggering the Metal texture allocation errors previously encountered on iOS. - Y-axis scrolling along with chart content: The library keeps the Y-axis fixed while allowing horizontal scrolling of large datasets, significantly improving readability and usability.
Key Props and Their Purpose
Before diving into custom enhancements, it's essential to understand a few key props that form the backbone of every chart:
barWidth
Controls the width of each bar. Using wp("5.5%") keeps it proportional to screen width, ensuring the chart looks balanced on both phones and tablets.
height and width
Defines the overall chart dimensions. Using percentage-based values (like height={wp("38%")}) makes the graph scale fluidly across devices.
spacing
Determines the horizontal space between bars. Dynamically adjusted using:
spacing={isTablet ? 70 : 65}
so that the chart maintains even spacing on different screen types.
noOfSections, stepValue, and maxValue
These three props control the scaling of the Y-axis:
-
noOfSections— Number of horizontal grid lines -
stepValue— Value difference between each section -
maxValue— The highest Y-axis value (multiply the maximum value by 1.2 to add a buffer, ensuring the tallest bar is never cropped at the top)
These three values must always satisfy:
maxValue = noOfSections * stepValue
formatYLabel
A function used to format the Y-axis values. Used to round decimal values and ensure clean, readable labels:
formatYLabel={(value) => Math.round(Number(value)).toString()}
xAxisLabelTextStyle and yAxisTextStyle
Controls the font size, alignment, and color of the X and Y-axis labels. Adjust font sizes for tablets and phones for better legibility.
frontColor, xAxisColor, and yAxisColor
-
frontColor— Sets the color of the bars -
xAxisColor&yAxisColor— Defines the axis line colors for a subtle and professional look
dashGap
Defines gaps between dashes in grid lines. Set to 0 to show solid lines behind the graphs.
Making the Charts Truly Dynamic
Now that the base graph is working, let's move on to the custom enhancements that improve accuracy, responsiveness, and flexibility.
Dynamic Y-Axis Width for Large Values
One of the first issues encountered was truncated Y-axis labels when large numerical values were displayed. The Y-axis width is calculated dynamically based on the number of digits in the largest value:
const getYAxisWidth = (maxValue) => {
const digits = maxValue.toString().length;
if (digits <= 3) return wp('8%');
if (digits <= 5) return wp('10%');
return wp('12%');
};
This approach:
- Prevents label truncation — The Y-axis automatically adjusts to fit larger values.
- Maintains proper alignment — Bars and axes remain visually consistent regardless of dataset size.
- Improves responsiveness — Works seamlessly across devices with different screen sizes.
Properly Calculating Chart Sections and Scaling
For the graph to render correctly, the three properties (noOfSections, stepValue, and maxValue) must always follow this mathematical relationship:
maxValue = noOfSections * stepValue
If this relationship is not maintained — for instance, when only one of the three values is adjusted manually — the chart may render incorrectly or not at all.
Note: The documentation advises reloading the app whenever these values or related props (
height,stepHeight, etc.) are modified, as the new configurations are sometimes applied only after a full reload.
Here's how to handle these configurations dynamically:
const calculateChartScaling = (data) => {
const maxDataValue = Math.max(...data.map((item) => item.value));
const bufferedMax = maxDataValue * 1.2;
const noOfSections = 5;
const stepValue = Math.ceil(bufferedMax / noOfSections / 10) * 10;
const maxValue = noOfSections * stepValue;
return { noOfSections, stepValue, maxValue };
};
Structuring the Data for Flexibility
In react-native-gifted-charts, the data prop accepts an array of objects, each representing a single bar or data point:
const chartData = [
{
value: 150,
label: 'KA-51-AF-4155',
topLabelComponent: () => (
<Text style={{ fontSize: isTablet ? 10 : 8, color: '#333' }}>150</Text>
),
},
{
value: 320,
label: 'MH-12-BT-9921',
topLabelComponent: () => (
<Text style={{ fontSize: isTablet ? 10 : 8, color: '#333' }}>320</Text>
),
},
];
Property breakdown:
-
value— Defines the height of the bar -
label— Sets the X-axis label -
topLabelComponent— A custom React component for displaying value labels above each bar
Our Graph in Action
Here's how the charts render across different devices:
The End Result
By combining the base setup from react-native-gifted-charts with these enhancements, we achieved:
- Smooth, crash-free performance on both iOS and Android, even with large datasets
- A fixed, properly aligned Y-axis for improved readability
- Dynamic scaling and sizing that adapts to any screen size or dataset
- Clean, customizable visuals ready for production use
3. Advanced: Creating Grouped Bar Graphs
In many scenarios, we need to display multiple metrics side-by-side for the same category — such as current vs expected values. The react-native-gifted-charts library makes it straightforward to implement grouped bar charts by structuring the data prop appropriately and customizing the top labels.
Data Structure for Grouped Bars
To implement a grouped bar chart, most of the setup remains the same as a standard bar chart. However, there are a few additional considerations for handling multiple bars per category:
-
spacing— Adds space between bars within a group to clearly separate metrics -
topLabelComponent— For grouped bars, dynamically render the label above the taller bar in each group -
frontColor— Used to differentiate bars within the same group
Example snippet for grouped data:
const groupedData = [
{
value: 400,
label: 'Vehicle A',
frontColor: '#4ABFF4',
topLabelComponent: () => <TopLabel value={400} compareValue={300} />,
},
{
value: 300,
frontColor: '#FFA07A',
topLabelComponent: () => null,
},
{
value: 520,
label: 'Vehicle B',
frontColor: '#4ABFF4',
topLabelComponent: () => <TopLabel value={520} compareValue={480} />,
},
{
value: 480,
frontColor: '#FFA07A',
topLabelComponent: () => null,
},
];
Dynamic Top Labels
For grouped bars, top labels must be carefully positioned to avoid overlapping. Here's a reusable component that automatically adjusts the label width and position based on the taller bar in the group:
const TopLabel = ({ value, compareValue }) => {
const isLow = value < compareValue;
const labelText = value.toString();
const labelWidth = isTablet
? Math.max(labelText.length * 8, 40)
: Math.max(labelText.length * 6, 30);
return (
<View style={{ width: labelWidth, alignItems: 'center' }}>
<Text
style={{
fontSize: isTablet ? 10 : 8,
color: isLow ? '#FF6347' : '#333',
fontWeight: '600',
}}
>
{labelText}
</Text>
</View>
);
};
Key points:
- Dynamic Positioning — The top label is placed above the bar with the larger value in the group, ensuring it doesn't overlap or look misaligned.
- Dynamic Width Calculation — Width is calculated based on text length and screen type (tablet vs mobile) to prevent clipping.
-
Conditional Styling — Using an
isLowflag, different colors indicate low vs high values, improving readability and visual cues.
Our Grouped Bar Graph in Action
Final Thoughts
Building responsive and performant graphs in React Native is about more than just aesthetics — it's about ensuring usability, speed, and adaptability across devices. The react-native-gifted-charts library offers a strong foundation, and with a few tailored enhancements like adaptive layouts and precise alignment, we can create production-ready graphs that deliver a seamless experience across Android, iOS, and tablets.
Originally published on GeekyAnts Blog




Top comments (0)