Search code examples
reactjschartsrecharts

How to display multiple lines in Recharts X-Axis tick label?


I am being asked to put long string values as my X axis tick labels.

I tried other charting libraries, but this one overall has worked the best for my situation. I just cannot figure out what to do with the x axis labels.

I tried to create a separate div and map through the values underneath the chart, but I can't maintain the alignment between the chart and the values when there are more than 7 or 8 x axis values.

I had a tickFormatter function that cut the string at a certain number of characters/hid the overflow and was set at an angle on the x axis, but they still aren't happy with that.

This is my code currently and I feel like it is so close to being workable and I just wish that I could get the text to wrap onto more lines.

Working with SVGs is extremely confusing to me and I've tried to write functions that return SVG text elements as a custom tick, but I couldn't get it to work. Or when I pass something and it returns [object Object] as the label.

 const data = React.useMemo(() => {
 return orderOfItems?.map((claim, i) => ({
  Item: i + 1,
  Claim: claim.split(" ").join("\n"),
  Reach: (
    incrementalReachSummary[claim]?.Summary_Metrics.Reach * 100
  ).toFixed(1),
}));
}, [orderOfItems, incrementalReachSummary]);

const tickFormatter = (value: string) => {
const limit = 10; // put your maximum character

if (value.length < limit) return value;
return `${value.split(" ").join("\n")}...`;
};

return (
 <>
  <ResponsiveContainer
    align={"end"}
    w={"60%"}
    height={200}
    aspect={2}
    minWidth={"undefined"}
    maxHeight={"undefined"}
  >
    <LineChart
      data={data}
      margin={{
        top: 50,
        right: 250,
        left: 250,
        bottom: 500,
      }}
    >
      <CartesianGrid
        strokeDasharray="3"
        opacity={colorMode === "dark" ? 0.3 : 0.9}
      />
      <XAxis
        // hide={true}
        dataKey={"Claim"}
        interval="preserveStartEnd"
        angle={-35}
        tickFormatter={tickFormatter}
        textAnchor={"end"}
        axisLine={false}
        offset={5}
        tickMargin={10}
        style={{
          fill: colorMode === "dark" ? "#FFFFFF" : "#1A202C",
          textAlign: "center",
        }}
      >
        {/*<Label value="Claim" offset={100} position="bottom" />*/}
      </XAxis>
      <YAxis
        type={"number"}
        domain={[0, 100]}
        axisLine={false}
        tickLine={false}
        tickCount={7}
        tickMargin={10}
        tickFormatter={(Reach) => `${Reach}%`}
        style={{ fill: colorMode === "dark" ? "#FFFFFF" : "#1A202C" }}
      />
      <Tooltip content={<CustomToolTip />} />
      <Line
        type="monotone"
        strokeWidth={3}
        dataKey="Reach"
        stroke="#3182CE"
        activeDot={{ r: 9 }}
      >
        <LabelList
          dataKey="Reach"
          offset={14}
          position="insideTopLeft"
          formatter={(Reach) => `${Reach}%`}
          fill={colorMode === "dark" ? "#FFFFFF" : "#1A202C"}
        />
      </Line>
    </LineChart>
  </ResponsiveContainer>

enter image description here


Solution

  • You need to create a custom tick react component. Line breaks will not work. You will need to use multiple <tspan> components with correct dy or vertical position. Also increase the chart bottom so lines are not hidden.

    Use payload.value to access the original value & create as many tspans required.

    <XAxis tick={<CustomizedTick />} />
    

    Example

    function CustomizedTick(props) {
        const { x, y, stroke, payload } = props;
        return (
            <g transform={`translate(${x},${y})`}>
            <text x={0} y={0} dy={16} fill="#666">
              <tspan textAnchor="middle" x="0">
                Line 1
              </tspan>
              <tspan textAnchor="middle" x="0" dy="20">
                Line 2
              </tspan>
              <tspan textAnchor="middle" x="0" dy="40">
                Line 3
              </tspan>
            </text>
          </g>
        );
      }
    

    Working codesandbox.