Chart legend
Free · MITShared interactive legend: aria-pressed toggle buttons with popping square, dot, line or dashed swatches, hidden-state strike-through, optional values and a hover channel that highlights a series in its chart.
Installation
$ npx shadcn@latest add https://goodui.dev/r/chart-legend.jsonThe file lands in components/goodui/chart-legend.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/chart-legend works too.
Usage
import { ChartLegend } from "@/components/goodui/chart-legend"
import { LineChart } from "@/components/goodui/line-chart"
export function ChannelsCard() {
const [hidden, setHidden] = React.useState<string[]>([])
const [active, setActive] = React.useState<string | null>(null)
const items = series.map((s, i) => ({ key: s.key, label: s.label, color: `var(--chart-${i + 1})`, shape: "line" as const }))
return (
<div className="rounded-xl border bg-card p-5">
<div className="mb-3 flex items-center justify-between">
<h3 className="text-sm font-medium">Bookings by channel</h3>
<ChartLegend items={items} hidden={hidden} onHiddenChange={setHidden} active={active} onActiveChange={setActive} size="sm" />
</div>
<LineChart categories={weeks} series={series} hidden={hidden} onHiddenChange={setHidden} legend="none" />
</div>
)
}When to use
- Any chart with two or more series; the goodui line, bar, donut and radial charts render it for you.
- A legend placed away from its chart (in a card header) that still toggles and highlights series.
- A plain key with values ("Card 58%") under a custom visual.
- A single series: the chart title names it; a one-swatch legend only costs space.
- Status meanings (healthy / critical): those need an icon and label, not a series key.
| If you need | Use |
|---|---|
| Toggle series | hidden + onHiddenChange (or defaultHidden); items become aria-pressed buttons |
| Highlight on hover | active + onActiveChange, mirror it into the chart |
| Static key with numbers | items with value, no handlers |
| Match the mark | shape="line" / "dashed" for lines, "square" for bars and areas, "dot" for points |
Examples
Static key with shares
<ChartLegend layout="column" shape="dot" items={[{ key: "card", label: "Card", color: "var(--chart-1)", value: "58%" }, { key: "cash", label: "Cash", color: "var(--chart-3)", value: "13%" }]} />Translated tooltips
<ChartLegend items={items} defaultHidden={[]} labels={{ legend: "Reihen", toggle: (label, hidden) => (hidden ? `${label} einblenden` : `${label} ausblenden`) }} />API reference
| Prop | Type | Default |
|---|---|---|
| items* | ChartLegendItem[] | — |
| hidden Keys of hidden items (controlled). | string[] | — |
| defaultHidden Hidden keys to start with when uncontrolled. | string[] | — |
| onHiddenChange Called with the new hidden keys after a toggle. | (hidden: string[]) => void | — |
| interactive Items toggle on click, Enter and Space. On by default when hidden, defaultHidden or onHiddenChange is set. | boolean | — |
| active Key of the highlighted item (controlled hover sync). | string | null | — |
| onActiveChange Called when the pointer or focus enters an item (key) or leaves (null). | (key: string | null) => void | — |
| shape Swatch shape: square for bars and areas, line or dashed for lines, dot for points. | "square" | "dot" | "line" | "dashed" | "square" |
| layout | "row" | "column" | "row" |
| align | "start" | "center" | "end" | "start" |
| size | "sm" | "md" | "md" |
| labels Text for built-in controls and screen readers; pass translations here. | Partial<ChartLegendLabels> | — |
Also accepts every <div> attribute.
Accessibility
| Tab | Moves between legend buttons (interactive legends only). |
| Enter / Space | Hides or shows that series. |
- Interactive items are real buttons with aria-pressed (true = shown); their accessible name is the visible label (and value), and labels.toggle supplies the title tooltip ("Hide Revenue" / "Show Revenue"). The group is labelled by labels.legend.
- Static legends are a list (role="list" / "listitem").
- Hidden items keep their text with a strike-through and a hollow swatch; colors are never the only state cue.
- Focus shows the ring token; hovering or focusing an item reports it through onActiveChange so charts can highlight it.
Motion
- Toggling a swatch scales it to 85% with an overshooting ease (0.3 s, cubic-bezier(.3,1.5,.5,1)); pressing a button squeezes its swatch to 80%. Text and swatch colors transition over 0.2 s. Pure CSS.
- Reduced motion: no scale, no transitions.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- The legend does not hide anything by itself: mirror `hidden` into the chart's hidden prop (the goodui charts accept the same string[]).
- Keys must match the chart's series keys. Colors are the series colors (CSS values), not Tailwind classes.
- A legend is interactive when hidden, defaultHidden or onHiddenChange is given, or interactive is true.
FAQ
Can I use it with Recharts or another library?
Yes; it is a plain React component. Keep hidden in state and filter your series with it.
Related
Multi-series lines that draw in staggered, a legend that toggles series while the scale refits, a crosshair that reads every series at once, and a dashed forecast segment behind a divider.
Grouped, stacked or horizontal bars that grow from the baseline on view and morph when data changes, with a gliding hover band, legend toggles, value labels and 2px surface gaps between segments.
Donut or pie whose slices sweep in clockwise, re-share the ring when a legend item is toggled, push out on hover and roll the center total to the slice under the pointer.
Activity-style concentric goal rings that fill from 12 o'clock on view, spring to new values, dim the others when one is pointed at, and roll the center percentage.