mrkeyoor.com_
Sat 08 Aug 21:00 UTC
npmWeb Frontendupdated 08 Aug 2026

@visx/shape

@visx/shape is the low-level SVG geometry layer of Airbnb's visx visualization toolkit. It turns data, accessors, scales, and curve choices into React elements for bars, lines, areas, pies, stacks, grouped bars, polygons, and tree links. It deliberately is not a finished charting product: you compose its primitives with scales, axes, responsive measurement, events, tooltips, animation, and accessible descriptions chosen for your application.

Verdict

One of the best foundations for bespoke React visualizations when your team wants to own the chart rather than configure one. It is the wrong first install for ordinary dashboards whose real requirement is a ready-made responsive chart with axes, tooltips, and accessible interaction.

API stability4/5The primitive component model and root exports are intentionally small, and the v4 migration guide says existing component APIs continue to work while documenting the supported import surface. The major still imposes real boundaries: React 18 or 19 is now required, matching React types are peers, D3 shape internals moved to v3, and deep imports are no longer blessed. Root imports and a pinned major should be dependable.
Docs3/5The project README clearly explains the low-level philosophy and includes a complete bar-chart example; the package page catalogs components and the gallery provides working compositions. The v4 migration guide is unusually concrete about peer dependencies and import changes. Individual shape documentation is closer to generated prop reference than a task guide, so stacks, responsive charts, tooltips, and accessibility still require reading examples across several visx packages.
Maintenance4/5Version 4.0.0 was published June 11, 2026, the repository was pushed June 22, and the project is not archived. The v4 work updated React support, ESM output, declarations, dependency internals, and migration documentation rather than merely republishing old code. GitHub reports 146 open issues and pull requests across the entire visx monorepo, so package-specific backlog cannot be inferred from that combined number.
Ecosystem5/5The package recorded 4,288,961 downloads for the measured week, and the visx repository has 20,999 stars. Shape is designed to compose with a large maintained family covering scales, axes, grids, responsive measurement, tooltips, text, gradients, geographic data, networks, and higher-level XY charts. React 18 and 19 declarations are included, Preact compatibility is documented, and underlying D3 concepts transfer directly.

Use it if

  • You need a custom React or Preact chart whose SVG markup, styling, interaction, and accessibility must remain under your control
  • You understand scales and SVG geometry and want D3's shape calculations without D3 selections mutating React-owned DOM
  • You are building reusable visualization components for a design system rather than dropping in a fixed dashboard chart
  • You want to install only selected visx packages and tree-shake unused shape exports
Skip it if

Setup reality

Install `@visx/shape`, React 18 or 19, and a matching `@types/react` package for TypeScript. There is no native build or required stylesheet. The package publishes CommonJS and ESM builds, bundled declarations, and `sideEffects: false`, but a useful chart normally adds other packages: `@visx/scale` for domains and ranges, `@visx/group` for margin transforms, `@visx/axis` for ticks, `@visx/responsive` for container measurement, and `@visx/tooltip` or `@visx/event` for interaction. Keep all `@visx/*` packages on the same v4 major; the v4 migration guide explicitly tells monorepo users to upgrade them together. Deep imports such as `@visx/shape/lib/shapes/Bar` are no longer the supported surface, so import from the package root. The real setup cost is chart math. SVG y coordinates increase downward, meaning a vertical scale commonly uses `[innerHeight, 0]`, and bar height is `innerHeight - yScale(value)`. Band scales may return `undefined`, so TypeScript forces you to handle that possibility. You also own invalid and missing data through `defined`, stable React keys, margins, clipping, resize behavior, event hit targets, labels, color contrast, keyboard access, and screen-reader summaries. Shape components pass ordinary SVG props through, which is flexible but makes it easy to create a pretty picture with no useful semantics. For animation, install and integrate your own React animation system. For Preact, alias React and React DOM to `preact/compat` and satisfy the declared peer range as the README instructs.

Patterns

Render a basic vertical bar chartdraw-bar-chart

import { Bar } from '@visx/shape';
import { scaleBand, scaleLinear } from '@visx/scale';

const x = scaleBand({ domain: data.map((d) => d.label), range: [0, width], padding: 0.2 });
const y = scaleLinear({ domain: [0, Math.max(...data.map((d) => d.value))], range: [height, 0] });

const bars = data.map((d) => {
  const barX = x(d.label) ?? 0;
  const barY = y(d.value);
  return <Bar key={d.label} x={barX} y={barY} width={x.bandwidth()} height={height - barY} fill="#2563eb" />;
});

SVG y grows downward. Map the numeric domain to `[height, 0]`, then subtract the scaled y position from the inner height.

Round only the tops of positive barsdraw-rounded-bars

import { BarRounded } from '@visx/shape';

<BarRounded
  x={barX}
  y={barY}
  width={barWidth}
  height={barHeight}
  radius={6}
  top
  fill="#0f766e"
/>

`BarRounded` renders a path rather than a rect. Its radius is clamped to half the shorter side, and corner flags determine which corners change.

Generate a curved line pathdraw-line-chart

import { LinePath } from '@visx/shape';
import { curveMonotoneX } from '@visx/curve';

<LinePath
  data={points}
  x={(d) => xScale(d.date)}
  y={(d) => yScale(d.value)}
  curve={curveMonotoneX}
  fill="none"
  stroke="#7c3aed"
  strokeWidth={2}
/>

Monotone interpolation avoids x-axis reversals but still changes the path between observations; use a linear curve when interpolation would imply unsupported precision.

Break a line around missing valuesskip-missing-points

<LinePath
  data={points}
  defined={(d) => d.value != null}
  x={(d) => xScale(d.date)}
  y={(d) => yScale(d.value ?? 0)}
  stroke="currentColor"
  fill="none"
/>

The `defined` accessor creates gaps. Filtering missing rows instead would connect observations across the gap and can misrepresent the series.

Fill an area down to the zero baselinedraw-area-chart

import { AreaClosed } from '@visx/shape';

<AreaClosed
  data={points}
  x={(d) => xScale(d.date)}
  y={(d) => yScale(d.value)}
  yScale={yScale}
  fill="rgba(37, 99, 235, 0.25)"
  stroke="#2563eb"
/>

`AreaClosed` needs the y scale so it can determine the baseline. Include zero in the scale domain unless a truncated baseline is intentional and clearly communicated.

Render a donut with explicit arc keysdraw-donut-chart

import { Pie } from '@visx/shape';

<Pie
  data={segments}
  pieValue={(d) => d.value}
  innerRadius={60}
  outerRadius={100}
  padAngle={0.01}
>
  {({ arcs, path }) => arcs.map((arc) => (
    <path key={arc.data.id} d={path(arc) ?? undefined} fill={color(arc.data.id)} />
  ))}
</Pie>

Use a stable data identifier as the React key. Pie angles encode part-to-whole poorly when users need close value comparisons, so prefer bars for that task.

Place labels at arc centroidslabel-pie-segments

<Pie
  data={segments}
  pieValue={(d) => d.value}
  outerRadius={radius}
  centroid={([x, y], arc) => (
    arc.endAngle - arc.startAngle > 0.25
      ? <text x={x} y={y} textAnchor="middle" dominantBaseline="middle">{arc.data.label}</text>
      : null
  )}
/>

Centroid labels can overlap or become unreadable on small arcs. Hide them below a threshold and provide the full values in adjacent text or an accessible table.

Generate grouped bar geometrydraw-grouped-bars

import { BarGroup } from '@visx/shape';

<BarGroup
  data={data}
  keys={keys}
  height={innerHeight}
  x0={(d) => d.category}
  x0Scale={categoryScale}
  x1Scale={seriesScale}
  yScale={valueScale}
  color={(key) => colors[key]}
/>

BarGroup expects a band scale for categories, another band scale for series keys within each category, and a value scale. Axes and legends are separate components.

Build a stacked bar chartdraw-stacked-bars

import { BarStack } from '@visx/shape';

<BarStack
  data={data}
  keys={keys}
  x={(d) => d.category}
  xScale={xScale}
  yScale={yScale}
  color={(key) => colorScale(key)}
  value={(d, key) => Number(d[key] ?? 0)}
/>

Set the y domain from the totals across all stack keys, not from the largest individual value, or the generated bars can exceed the chart bounds.

Use a shape's path generator in custom markupcustomize-generated-path

<LinePath data={points} x={(d) => x(d.x)} y={(d) => y(d.y)}>
  {({ path }) => (
    <path
      d={path(points) ?? undefined}
      fill="none"
      stroke="url(#line-gradient)"
      strokeWidth={3}
    />
  )}
</LinePath>

A render child replaces the component's default path element. You become responsible for the SVG props, accessibility hooks, and null path result.

Attach pointer and keyboard behavior to a baradd-bar-interaction

<Bar
  x={barX}
  y={barY}
  width={barWidth}
  height={barHeight}
  fill="#2563eb"
  tabIndex={0}
  role="img"
  aria-label={`${datum.label}: ${datum.value}`}
  onPointerMove={(event) => showTooltip(event, datum)}
  onFocus={(event) => showTooltip(event, datum)}
  onBlur={hideTooltip}
/>

Shape components forward SVG props, but tooltips and semantics are yours. Mirror pointer behavior for keyboard focus and provide a nonvisual summary for complex charts.

Keep shape coordinates inside chart marginstranslate-chart-margins

import { Group } from '@visx/group';

const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;

<svg width={width} height={height}>
  <Group left={margin.left} top={margin.top}>
    {renderShapes({ width: innerWidth, height: innerHeight })}
  </Group>
</svg>

Use inner dimensions for every scale range. Mixing outer width with a translated Group is a common cause of clipped marks and misaligned axes.

Alternatives

PackageRegistryPick it when
rechartsnpmChoose it when product teams want composed React charts with axes, legends, tooltips, and responsive containers supplied
@nivo/corenpmChoose Nivo's packages when polished chart components, themes, animation, and Canvas variants matter more than low-level SVG control
victorynpmChoose it for a higher-level declarative React chart API that also has a React Native story
d3-shapenpmChoose it when you only need path generators and do not want React components or the wider visx package family