Skip to main content
/
/
/
/
Composed chart

Composed chart

A composed chart is a chart that combines different chart types, such as lines, bars and areas in the same chart. This is a great way to visualize different types of data in the same chart.

Composed charts are a little more complex, and you may need to use different axes for different data series. In the example below, we have a composed line chart and bar chart, where the bar chart uses the left y-axis and the line chart uses an invisible right y-axis. You may want to show both y-axes, but for this example we thought it looked better with custom callout values on the line chart.

Composed chart example

Code

import { useEffect, useRef, useState } from "react";
import {
  XAxis,
  YAxis,
  Tooltip,
  CartesianGrid,
  Bar,
  ComposedChart,
  Line,
  LabelList,
  Legend,
} from "recharts";

import CustomTooltip from "../CustomToolTip";
import legendFormatter from "../legendFormatter";

const satisfactionData = [
  { respondents: 190, average: 4.4, year: 2021 },
  { respondents: 212, average: 4.3, year: 2022 },
  { respondents: 219, average: 4.2, year: 2023 },
  { respondents: 222, average: 4.3, year: 2024 },
];

function Callout({ x, y, value }: { x?: number; y?: number; value?: number }) {
  const textRef = useRef<SVGTextElement>(null);
  const [rectDimensions, setRectDimensions] = useState({
    width: 0,
    height: 0,
  });

  useEffect(() => {
    if (textRef.current) {
      const bbox = textRef.current.getBBox();
      setRectDimensions({ width: bbox.width + 16, height: bbox.height + 8 });
    }
  }, [value]);

  const { width, height } = rectDimensions;

  if (!x || !y || !value) {
    return null;
  }

  return (
    <g transform={`translate(${x}, ${y - height / 2})`}>
      <rect
        x={-width / 2}
        y={-height / 2 - 4}
        width={width}
        height={height}
        fill="var(--bfc-base)"
        opacity={0.5}
        rx={4} // Rounded corners
        ry={4} // Rounded corners
      />
      <text
        ref={textRef}
        x={0}
        y={-4}
        fill="var(--bfc-base-c-1)"
        fontSize={14}
        fontWeight={600}
        textAnchor="middle"
        dominantBaseline="middle"
      >
        {value}
      </text>
    </g>
  );
}

export default function ComposedChartExample() {
  return (
    <ComposedChart
      responsive
      height="100%"
      width="100%"
      data={satisfactionData}
      margin={{ left: -20 }}
    >
      <defs>
        <linearGradient id="colorRespondents" x1="0" y1="0" x2="0" y2="1">
          <stop offset="5%" stopColor="#A175FF" stopOpacity={1} />
          <stop offset="95%" stopColor="#614699" stopOpacity={1} />
        </linearGradient>
      </defs>
      <CartesianGrid
        strokeDasharray="5 5"
        vertical={false}
        stroke="var(--bfc-base-dimmed)"
      />
      <XAxis
        axisLine={false}
        tickLine={false}
        dataKey="year"
        tick={{ fill: "var(--bfc-base-c-2)" }}
        dy={8}
      />
      <YAxis
        yAxisId="respYAxis"
        dataKey="respondents"
        axisLine={false}
        tickLine={false}
        tick={{ fill: "var(--bfc-base-c-2)" }}
      />
      <YAxis hide yAxisId="avgYAxis" dataKey="average" domain={[1, 6]} />
      <Tooltip content={<CustomTooltip />} />
      <Legend align="right" formatter={legendFormatter} />
      <Bar
        yAxisId="respYAxis"
        name="Respondents"
        dataKey="respondents"
        fill="url(#colorRespondents)"
        radius={8}
      />
      <Line
        yAxisId="avgYAxis"
        name="Average score"
        dataKey="average"
        dot={false}
        type="monotone"
        stroke="var(--bfc-base-c)"
        strokeWidth={2}
      >
        <LabelList content={<Callout />} position="top" />
      </Line>
    </ComposedChart>
  );
}