Skip to content

Combo Chart

ComboChart combines bars and lines in one chart. Use it for dashboards where a current value, target, forecast, margin, or rate should be inspected together on the same timeline.

This chart is available in Chart Kit Pro. Install it once from Installation.

Revenue Operating View

Use a basic combo when the bar series explain the current month and the line series gives the surrounding target or forecast.

import { ComboChart } from "@chart-kit/pro";

const money = (value: number) => `$${Math.round(value)}k`;

const data = [
  { month: "Jan", revenue: 420, forecast: 480, margin: 128 },
  { month: "Feb", revenue: 560, forecast: 530, margin: 168 },
  { month: "Mar", revenue: 490, forecast: 610, margin: 151 },
  { month: "Apr", revenue: 720, forecast: 690, margin: 214 },
  { month: "May", revenue: 640, forecast: 760, margin: 193 },
  { month: "Jun", revenue: 880, forecast: 840, margin: 276 },
  { month: "Jul", revenue: 790, forecast: 920, margin: 244 },
  { month: "Aug", revenue: 1040, forecast: 980, margin: 331 }
];

export function RevenueOperations() {
  return (
    <ComboChart
      data={data}
      xKey="month"
      series={[
        { key: "bar-revenue", yKey: "revenue", label: "Revenue", type: "bar" },
        { key: "bar-margin", yKey: "margin", label: "Margin", type: "bar" },
        {
          key: "line-forecast",
          yKey: "forecast",
          label: "Forecast",
          type: "line"
        }
      ]}
      defaultSelectedIndex={5}
      formatYLabel={money}
      width={410}
      height={300}
    />
  );
}

Shared Tooltip

Use a persistent selected index when the chart should open with a meaningful month already inspected. interaction="tap" keeps the shared tooltip aligned with the selected x value.

import { ComboChart } from "@chart-kit/pro";

const money = (value: number) => `$${Math.round(value)}k`;

const bookings = [
  { month: "Jan", booked: 118, target: 132 },
  { month: "Feb", booked: 146, target: 140 },
  { month: "Mar", booked: 182, target: 168 },
  { month: "Apr", booked: 208, target: 196 },
  { month: "May", booked: 236, target: 224 },
  { month: "Jun", booked: 274, target: 252 }
];

export function PipelineInspection() {
  return (
    <ComboChart
      data={bookings}
      xKey="month"
      series={[
        { key: "bar-booked", yKey: "booked", label: "Booked", type: "bar" },
        { key: "line-target", yKey: "target", label: "Target", type: "line" }
      ]}
      defaultSelectedIndex={3}
      formatYLabel={money}
      interaction="tap"
      tooltip={{ width: 148 }}
      width={410}
      height={292}
    />
  );
}

Series Toggles

Keep series visibility in product state when the chart is part of broader dashboard controls. The chart keeps the remaining bars and lines aligned to the same x-axis.

import { useState } from "react";
import { Pressable, Text, View } from "react-native";
import { ComboChart } from "@chart-kit/pro";

const money = (value: number) => `$${Math.round(value)}k`;
const percent = (value: number) => `${Math.round(value)}%`;

const channelPlan = [
  { month: "Jan", direct: 44, enterprise: 128, margin: 17 },
  { month: "Feb", direct: 52, enterprise: 154, margin: 19 },
  { month: "Mar", direct: 58, enterprise: 178, margin: 21 },
  { month: "Apr", direct: 66, enterprise: 204, margin: 24 },
  { month: "May", direct: 72, enterprise: 224, margin: 27 },
  { month: "Jun", direct: 80, enterprise: 252, margin: 30 }
];

const toggleItems = [
  { key: "bar-direct", label: "Direct" },
  { key: "bar-enterprise", label: "Enterprise" },
  { key: "line-margin", label: "Margin" }
];

export function ChannelPlan() {
  const [visibleSeriesKeys, setVisibleSeriesKeys] = useState(
    toggleItems.map((item) => item.key)
  );

  const toggleSeries = (key: string) => {
    setVisibleSeriesKeys((currentKeys) => {
      const nextKeys = currentKeys.includes(key)
        ? currentKeys.filter((item) => item !== key)
        : [...currentKeys, key];

      return nextKeys.length > 0 ? nextKeys : currentKeys;
    });
  };

  return (
    <View>
      <View style={{ flexDirection: "row", flexWrap: "wrap", gap: 8 }}>
        {toggleItems.map((item) => {
          const active = visibleSeriesKeys.includes(item.key);

          return (
            <Pressable
              key={item.key}
              accessibilityRole="button"
              onPress={() => toggleSeries(item.key)}
              style={{
                borderColor: active ? "#2563eb" : "rgba(100, 116, 139, 0.32)",
                borderRadius: 999,
                borderWidth: 1,
                paddingHorizontal: 10,
                paddingVertical: 5
              }}
            >
              <Text style={{ color: active ? "#2563eb" : "#64748b" }}>
                {item.label}
              </Text>
            </Pressable>
          );
        })}
      </View>

      <ComboChart
        data={channelPlan}
        xKey="month"
        series={[
          { key: "bar-direct", yKey: "direct", label: "Direct", type: "bar" },
          {
            key: "bar-enterprise",
            yKey: "enterprise",
            label: "Enterprise",
            type: "bar"
          },
          { key: "line-margin", yKey: "margin", label: "Margin", type: "line" }
        ]}
        formatYLabel={(value) => (value > 40 ? money(value) : percent(value))}
        visibleSeriesKeys={visibleSeriesKeys}
        width={410}
        height={292}
      />
    </View>
  );
}

Negative Domain

Use an explicit y-domain when the bar series can cross zero. The line still shares the same selected x value, so recovery screens can show whether a leading indicator is improving before the bar metric turns positive.

import { ComboChart } from "@chart-kit/pro";

const signedMoney = (value: number) =>
  value < 0 ? `-$${Math.abs(Math.round(value))}k` : `$${Math.round(value)}k`;

const recovery = [
  { month: "Jan", profit: -22, cashFlow: 8 },
  { month: "Feb", profit: -8, cashFlow: 12 },
  { month: "Mar", profit: 14, cashFlow: 16 },
  { month: "Apr", profit: 32, cashFlow: 20 },
  { month: "May", profit: 48, cashFlow: 24 },
  { month: "Jun", profit: 38, cashFlow: 22 }
];

export function ProfitRecovery() {
  return (
    <ComboChart
      data={recovery}
      xKey="month"
      series={[
        { key: "bar-profit", yKey: "profit", label: "Profit", type: "bar" },
        {
          key: "line-cash-flow",
          yKey: "cashFlow",
          label: "Cash flow",
          type: "line"
        }
      ]}
      defaultSelectedIndex={4}
      formatYLabel={signedMoney}
      yDomain={{ min: "dataMin", max: "dataMax", nice: true }}
      width={410}
      height={292}
    />
  );
}

Product Use Cases

Use Combo charts for revenue vs forecast, spend vs acquisition, active users vs conversion, inventory vs sell-through, channel plan controls, and operational dashboards where one metric explains another.

Props

PropTypeDescription
dataTData[]Object-row source data.
xKeykeyof TDataRow key used for x-axis labels.
seriesComboChartSeries[]Mixed bar and line series configuration.
series[].keystringStable id used by visibility and selection state.
visibleSeriesKeysstring[]Optional list of series keys to render.
selectedIndexnumberControlled selected x value.
defaultSelectedIndexnumberInitial selected x value.
interaction"tap", "none", or objectSelection mode and callbacks.
tooltipboolean or objectShows and configures the shared tooltip.
yDomainobject or tupleOverrides or constrains the computed y-domain.
formatYLabel(value) => stringFormats y-axis and tooltip values.
widthnumberOuter chart width in pixels.
heightnumberOuter chart height in pixels.