Skip to content

Legend overlaps chart content on container resize (v3 regression) #7200

Description

@maroKanatani

What is the current behavior?

In recharts v3, when the browser window is resized from a wide width to a narrow width, the Legend overlaps with the chart content. Legend items wrap to multiple lines as the container narrows, increasing the Legend's height, but the chart area does not shrink to accommodate the taller Legend. This results in the Legend rendering on top of the chart bars and axis labels.

This is a regression from v2, where the chart area correctly adjusts when the Legend wraps.

v3 (3.8.1) — after resize from 1200px to 500px:

Image

v2 (2.15.4) — same resize, no overlap:

Image

What is the expected behavior?

When the Legend wraps to multiple lines due to container resize, the chart area should shrink accordingly (via offset recalculation), preventing any overlap between the Legend and chart content — as it does in v2.

Please provide a demo of the problem in a sandbox

Reproduction code (paste into a new project with recharts@3):

import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts';

const data = [
  { name: 'Jan', s1: 400, s2: 300, s3: 200, s4: 278, s5: 189, s6: 239, s7: 349, s8: 200, s9: 300, s10: 400, s11: 500, s12: 100 },
  { name: 'Feb', s1: 300, s2: 200, s3: 400, s4: 200, s5: 300, s6: 100, s7: 250, s8: 350, s9: 150, s10: 250, s11: 450, s12: 200 },
];

const colors = ['#8884d8','#82ca9d','#ffc658','#ff7300','#0088fe','#00c49f','#ffbb28','#ff8042','#a4de6c','#d0ed57','#8dd1e1','#83a6ed'];

function App() {
  return (
    <div style={{ width: '100%', height: 400 }}>
      <ResponsiveContainer width="100%" height="100%">
        <BarChart data={data}>
          <CartesianGrid strokeDasharray="3 3" />
          <XAxis dataKey="name" />
          <YAxis />
          <Tooltip />
          <Legend />
          {colors.map((c, i) => <Bar key={i} dataKey={`s${i+1}`} fill={c} name={`Data Series ${i+1}`} />)}
        </BarChart>
      </ResponsiveContainer>
    </div>
  );
}

Steps:

  1. Render the above component at a wide browser width (e.g. 1200px) so Legend fits in ~2 rows
  2. Resize the browser to a narrow width (e.g. 500px)
  3. v3 (3.8.1): Legend overlaps with chart bars and X-axis labels
  4. v2 (2.15.4): Chart area shrinks correctly, no overlap

Which versions of Recharts, and which browser / OS are affected by this issue? Did this work in previous versions of Recharts?

  • Recharts: v3.8.1 (bug present), v2.15.4 (works correctly — this is a v3 regression)
  • React: 18.2.0
  • Browser: Chrome 137
  • OS: macOS

Root cause analysis

Three independent bugs contribute to this issue:

Bug 1: useElementOffset does not re-measure on resize

File: src/util/useElementOffset.ts

In v2, Legend was a class component using componentDidUpdate()updateBBox(), which re-measured the Legend's bounding box on every render. In v3, useElementOffset uses a useCallback ref callback that only fires when React re-attaches the ref. When ResponsiveContainer triggers a resize, the Legend content may wrap to more lines (increasing height), but the ref callback is not re-invoked, so the new height is never measured.

Bug 2: LegendSettingsDispatcher / LegendSizeDispatcher use useEffect (post-paint)

File: src/component/Legend.tsx

Both dispatchers use useEffect to update Redux state with Legend settings and size. Since useEffect runs after paint, there is a frame where the chart renders without the correct Legend offset. Notably, SetLegendPayload.ts already uses useLayoutEffect for legend payload dispatch.

Bug 3: legendSlice initial state verticalAlign mismatch

File: src/state/legendSlice.ts

The Redux initial state has verticalAlign: 'middle', but Legend defaultProps use verticalAlign: 'bottom'. In appendOffsetOfLegend (src/util/ChartUtils.ts), the condition verticalAlign !== 'middle' means that when the initial state is 'middle', the offset adjustment for the Legend height is skipped entirely.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions