RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/Dev Tools
Dev Tools/2026-05-03Intermediate

react-native-chart-kit BarChart with Multiple Datasets: Side-by-Side Implementation Patterns for Rork Apps

react-native-chart-kit's BarChart doesn't support multiple datasets out of the box. Learn 3 practical workarounds for side-by-side grouped bar charts in Rork apps — from quick color-coded fixes to migrating to Victory Native.

React Native209react-native-chart-kitBarChartdata visualization2Rork515charts

When building a Rork app that needs to compare monthly income vs. expenses side by side, react-native-chart-kit's BarChart seems like the obvious choice. But once you actually try it, you run into a frustrating wall:

// This is what you want — but it won't work
const data = {
  labels: ["Jan", "Feb", "Mar"],
  datasets: [
    { data: [30000, 45000, 38000], color: () => "#4CAF50" }, // income
    { data: [22000, 31000, 29000], color: () => "#F44336" }, // expenses
  ],
};

Pass this to BarChart and only the first dataset renders. The second one is silently ignored. That's by design — react-native-chart-kit's BarChart internally only reads data.datasets[0], unlike LineChart which supports multiple datasets stacked on the same chart.

This is one of the library's most-searched limitations, and the GitHub Issues for it have stayed open for years. Here are three practical approaches to get grouped bar charts working in your Rork app.

Why chart-kit's BarChart Only Renders One Dataset

Looking at the source code, BarChart.tsx only iterates over data.datasets[0].data. The LineChart component handles multiple datasets differently, which makes them behave inconsistently despite being in the same library.

As of 2026, the core BarChart implementation still hasn't addressed this. Your options are to work around it, or switch libraries.

Approach 1: Color-Coded Simulation (Fastest Fix)

The simplest workaround is to interleave all data points into a single array and use the colors property to distinguish them visually. Not true grouped bars, but it works well for 3–4 months of comparisons.

import { BarChart } from "react-native-chart-kit";
import { Dimensions } from "react-native";
 
const screenWidth = Dimensions.get("window").width;
 
// Interleave income and expense values
const data = {
  labels: ["Jan↑", "Jan↓", "Feb↑", "Feb↓", "Mar↑", "Mar↓"],
  datasets: [
    {
      data: [30000, 22000, 45000, 31000, 38000, 29000],
      // Even indices = income (green), odd = expenses (red)
      colors: [
        () => "#4CAF50",
        () => "#F44336",
        () => "#4CAF50",
        () => "#F44336",
        () => "#4CAF50",
        () => "#F44336",
      ],
    },
  ],
};
 
export default function IncomeExpenseChart() {
  return (
    <BarChart
      data={data}
      width={screenWidth - 32}
      height={220}
      yAxisLabel="$"
      yAxisSuffix=""
      withCustomBarColorFromData={true}  // Required for colors array to work
      flatColor={true}                   // Required alongside the above
      chartConfig={{
        backgroundColor: "#ffffff",
        backgroundGradientFrom: "#ffffff",
        backgroundGradientTo: "#ffffff",
        decimalPlaces: 0,
        color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
        labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
      }}
      style={{ borderRadius: 8 }}
    />
  );
}

Key gotcha: Both withCustomBarColorFromData={true} and flatColor={true} must be set together. Either one alone won't apply the colors array — this is a common source of confusion.

Limitation: Labels get crowded quickly. Works fine for 2–3 month comparisons, but breaks down at 12 months.

Approach 2: Overlapping BarCharts with Absolute Positioning

Stack two BarChart instances using position: "absolute" to simulate grouped bars. More visual flexibility, but fiddly to get right.

import { View } from "react-native";
import { BarChart } from "react-native-chart-kit";
import { Dimensions } from "react-native";
 
const screenWidth = Dimensions.get("window").width;
const chartWidth = screenWidth - 32;
 
const baseConfig = {
  backgroundColor: "transparent",
  backgroundGradientFrom: "transparent",
  backgroundGradientTo: "transparent",
  decimalPlaces: 0,
  color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
  labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
};
 
const incomeData = {
  labels: ["Jan", "Feb", "Mar"],
  datasets: [{ data: [30000, 45000, 38000] }],
};
 
const expenseData = {
  labels: [" ", " ", " "], // Blank labels to avoid double axis
  datasets: [{ data: [22000, 31000, 29000] }],
};
 
export default function OverlayBarChart() {
  return (
    <View style={{ height: 220, position: "relative" }}>
      {/* Background: income with visible axes */}
      <BarChart
        data={incomeData}
        width={chartWidth}
        height={220}
        yAxisLabel="$"
        yAxisSuffix=""
        chartConfig={{
          ...baseConfig,
          color: () => "#4CAF50",
          backgroundColor: "#ffffff",
          backgroundGradientFrom: "#ffffff",
          backgroundGradientTo: "#ffffff",
        }}
        style={{ position: "absolute", left: 0, top: 0 }}
      />
      {/* Foreground: expenses with transparent background */}
      <BarChart
        data={expenseData}
        width={chartWidth}
        height={220}
        yAxisLabel=""
        yAxisSuffix=""
        chartConfig={{
          ...baseConfig,
          color: () => "#F44336",
        }}
        style={{ position: "absolute", left: 0, top: 0 }}
      />
    </View>
  );
}

Honestly, aligning the Y-axes between the two charts takes more trial-and-error than it looks. If you find yourself spending more than an hour on this, the next approach is worth considering.

Approach 3: Switch to Victory Native (Recommended for Grouped Bars)

The more you work around react-native-chart-kit's BarChart limitations, the more you realize that Victory Native solves this natively. VictoryGroup handles grouped bars with a clean API, and the result looks polished without any hacks.

import {
  VictoryBar,
  VictoryChart,
  VictoryGroup,
  VictoryAxis,
  VictoryTheme,
} from "victory-native";
 
const incomeData = [
  { x: "Jan", y: 30000 },
  { x: "Feb", y: 45000 },
  { x: "Mar", y: 38000 },
];
 
const expenseData = [
  { x: "Jan", y: 22000 },
  { x: "Feb", y: 31000 },
  { x: "Mar", y: 29000 },
];
 
export default function GroupedBarChartVictory() {
  return (
    <VictoryChart
      theme={VictoryTheme.material}
      domainPadding={{ x: 30 }}
      height={300}
    >
      <VictoryAxis
        tickValues={["Jan", "Feb", "Mar"]}
        style={{ tickLabels: { fontSize: 12, fill: "#333" } }}
      />
      <VictoryAxis
        dependentAxis
        tickFormat={(t) => `$${(t / 1000).toFixed(0)}k`}
        style={{ tickLabels: { fontSize: 11, fill: "#333" } }}
      />
      <VictoryGroup
        offset={15}
        colorScale={["#4CAF50", "#F44336"]}
      >
        <VictoryBar data={incomeData} />
        <VictoryBar data={expenseData} />
      </VictoryGroup>
    </VictoryChart>
  );
}

Install Victory Native with:

npx expo install victory-native react-native-svg

For a full dashboard implementation with Victory Native including line charts, pie charts, and touch interactions, see Building Interactive Charts in Rork Apps with Victory Native.

Which Approach to Choose

If you already have react-native-chart-kit throughout your project and just need grouped bars in one or two places, Approach 1 (color-coded interleaving) is the fastest path to a working implementation.

If you're planning to add more chart types over time — or you're building a data-heavy app like a budget tracker, fitness dashboard, or analytics screen — this is a good moment to migrate to Victory Native. Running two chart libraries in parallel adds bundle size and maintenance overhead that adds up over time.

The choice depends on where your project is headed. For a quick prototype or MVP, Approach 1 is fine. For anything production-grade with ongoing chart requirements, Victory Native gives you a more stable foundation.

Start with Approach 1 to get something on screen quickly, then evaluate whether Victory Native makes sense for your full implementation. That tends to be a better use of development time than polishing a workaround that may need replacing anyway.

Common chart-kit BarChart Pitfalls to Avoid

While working through these approaches, a few other chart-kit gotchas come up regularly in Rork projects.

The segments prop affects Y-axis grid lines, not data grouping

Some developers find the segments prop and assume it controls how data is grouped horizontally. It doesn't — segments only controls how many horizontal grid lines appear on the Y-axis. Setting segments={2} when you have two datasets won't group them.

fromZero can distort visual comparisons

The fromZero={true} prop forces the Y-axis to start at zero, which is usually the right call for bar charts (starting mid-range makes differences look exaggerated). Without it, chart-kit may start the Y-axis at a value that makes two similar bars look dramatically different.

<BarChart
  data={data}
  fromZero={true}  // Always include this for bar charts
  // ...other props
/>

showValuesOnTopOfBars and long numbers

If you enable showValuesOnTopOfBars={true} with currency values in the tens of thousands, the labels will overflow the chart bounds. Either abbreviate the values in your data (e.g., 30 instead of 30000 and adjust the Y-axis suffix), or use yAxisLabel and yAxisSuffix to format the axis instead.

// Instead of raw values, normalize to thousands
const data = {
  labels: ["Jan", "Feb", "Mar"],
  datasets: [{
    data: [30, 45, 38], // values in thousands
  }],
};
 
<BarChart
  data={data}
  yAxisLabel="$"
  yAxisSuffix="k"
  showValuesOnTopOfBars={true}
  // Now labels show "$30k" without overflow
/>

Adding a Legend Manually

Neither the color-coded simulation nor the overlay approach gives you an automatic legend. For any chart that needs one, building a simple custom legend is straightforward:

import { View, Text, StyleSheet } from "react-native";
 
function ChartLegend() {
  return (
    <View style={styles.legendContainer}>
      <View style={styles.legendItem}>
        <View style={[styles.dot, { backgroundColor: "#4CAF50" }]} />
        <Text style={styles.legendLabel}>Income</Text>
      </View>
      <View style={styles.legendItem}>
        <View style={[styles.dot, { backgroundColor: "#F44336" }]} />
        <Text style={styles.legendLabel}>Expenses</Text>
      </View>
    </View>
  );
}
 
const styles = StyleSheet.create({
  legendContainer: {
    flexDirection: "row",
    justifyContent: "center",
    gap: 20,
    marginTop: 8,
  },
  legendItem: {
    flexDirection: "row",
    alignItems: "center",
    gap: 6,
  },
  dot: {
    width: 10,
    height: 10,
    borderRadius: 5,
  },
  legendLabel: {
    fontSize: 13,
    color: "#555",
  },
});

Place this directly below your BarChart component. It's a simple addition but makes the chart much more readable for users who haven't seen the data before.

When to Stick with chart-kit vs. When to Move On

react-native-chart-kit is a solid choice when you need simple, quickly-styled charts and aren't planning to go deep on data visualization. Its API is approachable, the default styles look clean, and for basic line charts or single-dataset bar charts, it covers most use cases without much configuration.

The grouped bar chart limitation is a real friction point, but it's also a useful signal: if your app's data requirements are getting complex enough that you need multiple datasets side by side, you're likely also reaching the point where you'd want interaction handling, custom tooltips, or animated transitions — features that Victory Native handles more gracefully.

There's no shame in starting with react-native-chart-kit for an MVP and migrating later. Just be aware that the migration path exists, and don't spend more than a day trying to force chart-kit to do something it wasn't built for.

If you're already on Victory Native, the grouped bar example in Approach 3 is production-ready as-is. Adjust the offset value to fine-tune bar spacing, and use VictoryTooltip with labelComponent if you want tap-to-reveal values on each bar.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-04-02
Building a Survey App with Rork — A Beginner's Guide to Forms, Data Collection, and Charts
Learn how to build a survey collection app from scratch using Rork. This beginner-friendly guide covers form design, multiple answer types, Supabase data storage, and aggregation charts — all in a single day.
Dev Tools2026-07-15
The Comma That Fell to the Start of a Line: Rork, Quote Cards, and Japanese Line Breaking
React Native's Text component does not guarantee Japanese line-breaking rules. Here are the violation rates I measured, a WORD JOINER implementation that survives copy and VoiceOver, an onTextLayout audit harness, and how Rork Max (SwiftUI) differs.
Dev Tools2026-07-15
My Rork sleep timer faded out on time — but the sound didn't
Rork writes sleep-timer fades against the wall clock with setInterval. The numbers say 30 minutes, but the real audio fades early or cuts out abruptly. Here is how to drive the fade from the actual playback position, why a logarithmic curve sounds natural, and the honest limit of JS fades when the screen is locked.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →