mrkeyoor.com_
Tue 22 Sept 22:33 UTC
npmWeb Frontendupdated 22 Sept 2026

@visx/shape review

@visx/shape 4.0.0 turns data, accessors, scales, and curve choices into React SVG marks and paths. Its exports cover bars, lines, areas, pies, stacks, polygons, and several link geometries. It is deliberately below the chart level: axes, legends, responsive measurement, tooltips, animation, keyboard behavior, and a screen-reader summary come from other packages or your code. Version 4 requires React 18 or 19, upgrades its D3 shape/path layer, publishes strict-ESM-compatible output, removes lodash from the package, and restricts supported imports to the package root. Deep `lib/shapes/*` imports used by older apps must change.

Verdict

@visx/shape 4.0.0 installed in 4.8 seconds, used 8 MB, bundled to 14.5 KB gzipped, and had 0 audit findings in our sandbox. Choose it for bespoke React SVG charts when the team will own the whole chart; ordinary dashboards should start with Recharts or Nivo.

We installed it

Lab card: what happened when we installed @visx/shapeScreenshot of @visx/shape documentation
Install✓ · 4.8s33 packages on disk · 8 MB
ImportESM import works · require() works · CommonJS package with exports map
Browser14.5 KBgzipped (45.4 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does @visx/shape install cleanly?

Yes. In a fresh container with an empty cache, npm install @visx/shape finished in 5 seconds, leaving 33 packages and 8 MB on disk. npm audit reported no known vulnerabilities.

How much does @visx/shape add to a browser bundle?

14.5 KB gzipped (45.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does @visx/shape work with both ESM and CommonJS?

Yes. Both import '@visx/shape' and require('@visx/shape') worked in Node 22 in our run. The package is published as CommonJS with an exports map.

Does @visx/shape include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

@visx/shape or recharts: which should you use?

recharts: Use it when product teams need ready-composed React charts with axes, legends, tooltips, and responsive containers. @visx/shape 4.0.0 installed in 4.8 seconds, used 8 MB, bundled to 14.5 KB gzipped, and had 0 audit findings in our sandbox.

When should you not use @visx/shape?

The requirement is a responsive chart with axes, legend, tooltip, theme, and accessibility out of the box. @visx/shape supplies none of those chart-level pieces.

API stability4/5The package keeps a small component model built around data, accessors, scales, SVG props, and render callbacks. Version 4 preserves those concepts and root exports while drawing a firm boundary around internals. It also raises peers to React 18 or 19, moves D3 shape and path dependencies to version 3, removes runtime PropTypes, modernizes browser targets, and blocks deep imports. Pin the major and keep all visx packages aligned.
Docs3/5The project README builds a complete bar chart and explains why visx keeps visualization primitives low level. The shape reference lists component props, the gallery demonstrates larger compositions, and the version 4 migration guide covers peers, root imports, ESM output, old browsers, D3 changes, and removed lodash. A reader still has to combine several package guides to learn responsive sizing, axes, tooltips, animation, and accessibility for one finished chart.
Maintenance4/5Version 4.0.0 shipped June 11, 2026 and the unarchived repository was pushed on June 22. GitHub reports 148 open issues and pull requests across the monorepo. The release updates React support, package exports, ESM behavior, D3 internals, security resolutions, types, test tooling, and migration documentation. There is no later stable release yet, so version 4 has less field time than the long-running version 3 line.
Ecosystem5/5The npm endpoint counted 5,247,725 downloads in the latest completed week, and GitHub reports 21,021 stars. Shape composes with maintained visx packages for scales, axes, grids, groups, responsive sizing, tooltips, text, curves, geographic data, and higher-level XY charts. Its concepts match SVG and D3, React 18 and 19 are supported, Preact compatibility is documented, and direct D3 packages remain available when a component wrapper is unnecessary.

Use it if

  • A React design system needs custom SVG charts whose markup, layout, interaction, and visual rules must remain under product control.
  • The team understands scale domains, SVG coordinates, accessors, baselines, and missing-data policy and wants D3 math without D3 DOM selections.
  • Only selected visx packages should be installed instead of adopting a finished charting framework.
  • The application needs bars, paths, stacks, pies, or tree links as composable React primitives rather than one fixed chart API.
Skip it if

Setup reality

We installed @visx/shape 4.0.0 in a fresh Node 22 Bookworm sandbox. npm finished in 4.8 seconds, left 33 packages, and occupied 8 MB on disk. The package is 896 KB unpacked with 5 direct dependencies and 2 peers. npm audit found 0 known vulnerabilities. It includes TypeScript declarations.

Version 4 publishes CommonJS and ESM entries behind an exports map; both require() and import worked in our checks. React 18 or 19 and matching @types/react satisfy the peer contract. Import from @visx/shape, since deep paths are blocked as private internals. Upgrade every @visx/* dependency to the same major. No stylesheet, credential, or native build is required.

A useful chart usually adds scale, axis, responsive, tooltip, text, or group packages. Our full namespace browser bundle measured 45.4 KB minified and 14.5 KB gzipped. Root exports and sideEffects: false allow a production bundler to remove unused shapes, but inspect the final application bundle. SVG y coordinates grow downward, band scales may return undefined, and bar heights need a chosen baseline.

Shape components pass SVG props through and leave semantics to the caller. Add a title and description, a table or textual equivalent, keyboard reachability for interactive marks, visible focus, and pointer targets large enough to use. Missing points need a defined policy; filtering them can falsely connect separate observations. Preact needs React aliases plus peer-range configuration. Legacy browsers such as IE11 are outside version 4's target, according to the migration guide.

Patterns

Place bars with band and linear scales draw-bar-chart

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

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

const marks = rows.map(d => {
  const top = y(d.value);
  return <Bar key={d.label} x={x(d.label) ?? 0} y={top} width={x.bandwidth()} height={innerHeight - top} fill="#2563eb" />;
});

SVG y coordinates increase downward. Reverse the y range and subtract the scaled position from the inner height.

Round the top of a positive bar round-bar-corners

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

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

`BarRounded` emits a path rather than a rect. The radius cannot exceed half of the shorter side.

Render a monotone line through observations draw-line

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 reversing along x, yet it still invents a curve between samples. Use a linear curve when that implication is misleading.

Leave gaps where observations are missing break-on-missing-data

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

The `defined` accessor breaks the path. Filtering missing rows would connect the points on either side and hide the gap.

Fill an area down to the scale baseline draw-area

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` uses the y scale to find its baseline. Include zero in the domain unless a truncated baseline is an explicit design choice.

Render keyed donut segments draw-donut

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

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

Use stable data identifiers as React keys. A donut is poor at close comparisons, so choose bars when exact ranking matters.

Hide labels on narrow arcs label-pie

<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">{arc.data.label}</text>
      : null
  }
/>

Centroid labels overlap on small segments. Put every label and value in adjacent text or a data table even when the SVG hides it.

Generate side-by-side bar groups group-bars

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

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

This requires one band scale for categories, another for series inside each category, and a value scale. Axes and legends are separate.

Generate stacked bar segments stack-bars

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

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

Calculate the y domain from each row's total across stack keys. Using the largest single segment can push the stack beyond the chart.

Take over the generated SVG path customize-path

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

A render child replaces the default element. Handle a null path result and add your own SVG semantics and event props.

Mirror pointer behavior on keyboard focus add-accessible-interaction

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

SVG props pass through, but visx does not create chart semantics. Add a chart description and nonvisual data representation beyond per-mark labels.

Keep scales inside translated chart margins apply-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}>
    {renderMarks({ width: innerWidth, height: innerHeight })}
  </Group>
</svg>

Every scale range should use inner dimensions. Mixing outer width with a translated group clips marks and shifts axes.

Alternatives

PackageRegistryPick it when
rechartsnpmUse it when product teams need ready-composed React charts with axes, legends, tooltips, and responsive containers.
@nivo/corenpmUse Nivo when themed chart families, animation, and Canvas variants matter more than direct control over SVG geometry.
victorynpmUse it for a higher-level declarative chart API with an established React Native path.
d3-shapenpmUse it when path generators are enough and React components or the wider visx package family add no value.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.