Skip to content

Latest commit

Β 

History

History
513 lines (356 loc) Β· 22.2 KB

File metadata and controls

513 lines (356 loc) Β· 22.2 KB

Upgrading to visx 4

visx 4 is the current major release. It modernizes React support, package entry points, browser targets, and the published ESM output. For most apps, the upgrade is:

  1. Upgrade all @visx/* packages together to ^4.0.0.
  2. Use React 18 or 19.
  3. Replace deep imports like @visx/shape/lib/shapes/Bar with package-root imports like @visx/shape.
  4. If you use TypeScript, install @types/react and, when needed, @types/react-dom using the major version that matches your React version.
  5. If you use withBoundingRects, attach its injected nodeRef to the DOM element you want measured.
  6. If you rely on generated runtime propTypes, migrate those checks into your app.
  7. If you still need IE11, stay on visx 3.

From visx 3.x

Upgrade every visx package in your application at the same time. Mixing visx 3 and visx 4 packages is not recommended because package entry points, peer dependency ranges, and internal package dependencies changed together.

npm install @visx/shape@^4 @visx/scale@^4

With Yarn, upgrade the visx packages your app depends on to the same major:

yarn up "@visx/*@^4.0.0"

React 18 or 19 is required

React-based @visx/* packages declare react peer dependencies as ^18.0.0 || ^19.0.0. Packages that depend on react-dom declare the same supported range for their react-dom peer dependency.

What you need to do:

  • React 18 or 19 consumers: no runtime code changes are expected for this peer dependency update.
  • React 16 or 17 consumers: stay on visx 3 or upgrade your app to React 18+ before installing visx 4.
  • Preact consumers: continue aliasing react and react-dom to preact/compat, and configure your package manager to satisfy the React 18/19 peer dependency range.

TypeScript React types are peer dependencies

React-based @visx/* packages now declare @types/react as an optional peer dependency instead of bundling their own copy. Packages that touch React DOM APIs also declare optional @types/react-dom peer dependencies.

What you need to do:

  • TypeScript consumers: install @types/react and, when needed, @types/react-dom using the major version that matches your React version.
  • Non-TypeScript consumers: no action needed.

Runtime PropTypes were removed

visx packages no longer depend on prop-types, and the build no longer generates runtime Component.propTypes from TypeScript definitions.

What you need to do:

  • Most consumers do not need to change code.
  • If your app imported prop-types only because a visx package installed it transitively, add prop-types as a direct dependency.
  • If your app inspects visx component propTypes at runtime, migrate that validation into your app or use TypeScript/static checks instead.

Package root imports are the supported API surface

Every package now publishes an exports field mapping package-root imports to types, import, and require entries. This improves module resolution in modern bundlers and Node.js, but it also restricts access to internal paths.

What you need to do: replace deep imports like @visx/foo/lib/bar with package-root imports like @visx/foo. If a symbol you need is not exported from the package root, open an issue.

Most common component prop types are now exported from package roots as TypeScript-only exports. For example, prefer importing AxisProps from @visx/axis instead of reaching into internal files.

Modern browser targets

Babel targets were bumped to modern browsers. IE11 and other legacy browsers are no longer supported.

What you need to do: if you still need IE11, stay on visx 3.

Published ESM output works in strict ESM environments

The published esm/ output now emits explicit .js extensions on relative imports and a nested esm/package.json with "type": "module". Strict Node ESM consumers such as Vite SSR, Deno, and edge runtimes should no longer hit ERR_MODULE_NOT_FOUND from extensionless visx ESM imports.

What you need to do: nothing, unless you previously pinned to an older alpha to work around ESM issues. Upgrade all visx packages to ^4.0.0.

D3 shape and path dependencies were upgraded

visx packages now use d3-shape v3 and d3-path v3 through @visx/vendor. visx preserves its existing package-level behavior where compatibility shims were needed, but direct D3 imports are now your app's responsibility.

What you need to do:

  • Most consumers do not need to change code.
  • If your app imports d3-shape or d3-path without declaring them in your own package.json, add them as direct dependencies.
  • If your app imports from @visx/vendor/d3-shape or @visx/vendor/d3-path, review the D3 v3 API and type changes.

@visx/bounds withBoundingRects requires nodeRef

withBoundingRects no longer falls back to ReactDOM.findDOMNode. The wrapped component receives a nodeRef prop and must attach it to the DOM element that should be measured. This keeps @visx/bounds compatible with React 19 and React strict-mode expectations.

What you need to do:

  • If you use withBoundingRects, make sure the wrapped component accepts nodeRef and passes it to the measured DOM element.
  • If you use TypeScript, type nodeRef as a React ref for the element you measure.
  • If you already attach nodeRef, no action is needed.
- function Tooltip({ rect, parentRect, children }) {
-   return <div>{children}</div>;
+ type TooltipProps = Omit<WithBoundingRectsProps, 'nodeRef'> & {
+   nodeRef?: React.Ref<HTMLDivElement>;
+ };
+
+ function Tooltip({ rect, parentRect, nodeRef, children }: TooltipProps) {
+   return <div ref={nodeRef}>{children}</div>;
  }

@visx/responsive measurement fixes

ParentSize and withParentSize now render a two-div structure to prevent infinite height growth in flex and grid layouts. useParentSize now uses a callback ref internally and also accepts an optional externalRef.

What you need to do:

  • Most consumers do not need to change code.
  • If you access parentRef.current from useParentSize, replace it with the returned node.
  • If tests or CSS rely on the exact ParentSize wrapper DOM structure, update them for the nested measurement div.

@visx/xychart axis loading behavior

BaseAxis no longer renders until at least one data series with non-empty data has been registered in DataContext. Previously, on initial render the axis could use the fallback scaleLinear() domain [0, 1], causing tickFormat to receive incorrect intermediate values before real data loaded.

What you need to do:

  • Most consumers do not need to change code.
  • If you relied on the axis being visible before data loaded, render a placeholder <Axis /> from @visx/axis directly with your own scale until your data is ready, then switch to the xychart <Axis />.

@visx/xychart withRegisteredData was removed

The internal withRegisteredData enhancer was removed from @visx/xychart. Series components now register their data directly instead of using that higher-order component.

What you need to do: most consumers do not need to change code. If you imported @visx/xychart/lib/enhancers/withRegisteredData directly, migrate to the public xychart series components and package-root exports.

Lodash is no longer a transitive implementation dependency

@visx/responsive, @visx/text, @visx/xychart, and @visx/shape no longer declare direct lodash or @types/lodash dependencies. Internal debounce, memoize, and chunk usage has been replaced with small local helpers. Public visx APIs are intended to behave the same.

What you need to do: if your app imported lodash without declaring it in your own package.json, add lodash as a direct dependency. It may have worked before only because a visx package installed it transitively.

Building visx from source requires newer tooling

This note is for contributors or consumers building the visx repo itself. The repository now uses Yarn 4 via Corepack and requires Node 24 or newer.

What you need to do: if you only install published @visx/* packages, no action is needed. If you build the repo locally, use Node 24+ and run Yarn through Corepack.

From 4.0.0 alpha releases

Upgrade all @visx/* packages to ^4.0.0. The stable release includes all alpha changes listed below, plus the final React 18/19 peer dependency range and Dependabot cleanup from the final alpha publishes.

If you installed 4.0.0-alpha.10, 4.0.0-alpha.12, or 4.0.0-alpha.13, upgrade immediately. Those alpha releases partially failed and left some packages unpublished or out of sync.

Alpha release history

The notes below are preserved for users who tested the v4 alpha series. Stable upgrade guidance above supersedes any older alpha-specific instructions.

4.0.0-alpha.17

Internal only: updated transitive brace-expansion lockfile entries to resolve a Dependabot security alert. No consumer-facing visx API changes are expected.

4.0.0-alpha.16

Internal only: updated demo, test, release, and build-tool dependencies to resolve Dependabot security alerts. No consumer-facing visx API changes are expected.

4.0.0-alpha.15

React 18 or 19 is now required

React-based @visx/* packages now declare react peer dependencies as ^18.0.0 || ^19.0.0. Packages that depend on react-dom declare the same supported range for their react-dom peer dependency. Optional @types/react and, where applicable, @types/react-dom peer dependencies use the same supported major-version range.

What you need to do:

  • React 18 or 19 consumers: no runtime code changes are expected for this peer dependency update.
  • React 16 or 17 consumers: stay on visx v3 or upgrade your app to React 18+ before installing visx v4.
  • TypeScript consumers: install @types/react and, when needed, @types/react-dom using the major version that matches your React version.
  • Preact consumers: continue aliasing react and react-dom to preact/compat, and configure your package manager to satisfy the React 18/19 peer dependency range.

4.0.0-alpha.14

Re-publish of alpha.12 and alpha.13, which partially failed (see below). No intended code changes relative to alpha.12 β€” all packages are now published at the same version.

What you need to do: if you installed any @visx/* packages at 4.0.0-alpha.12 or 4.0.0-alpha.13, upgrade to alpha.14 to ensure all packages are in sync.

Lodash removed from package dependencies

@visx/responsive, @visx/text, @visx/xychart, and @visx/shape no longer declare direct lodash or @types/lodash dependencies. Internal debounce, memoize, and chunk usage has been replaced with small local helpers. Public visx APIs are intended to behave the same, including the existing debounce options in @visx/responsive.

What you need to do:

  • Most consumers: nothing β€” this removes an implementation dependency from visx packages.
  • If your app imported lodash without declaring it in your own package.json: add lodash as a direct dependency. It may have worked before only because a visx package installed it transitively.

This release also fixes the XYChart docs/demo example so it passes its known width into XYChart. No consumer code changes are needed for that docs-only fix.

4.0.0-alpha.13 (broken)

⚠️ Do not use this release. An npm provenance transparency log conflict caused lerna publish to fail partway through. Some packages were never published at this version while the rest were, resulting in a version mismatch across the monorepo. Use alpha.14 instead.

4.0.0-alpha.12 (broken)

⚠️ Do not use this release. An npm provenance transparency log conflict caused lerna publish to fail partway through. Some packages were never published at this version while the rest were, resulting in a version mismatch across the monorepo. Use alpha.14 instead.

4.0.0-alpha.11

Re-publish of alpha.10 which partially failed (see below). No code changes β€” all packages are now published at the same version.

What you need to do: if you installed any @visx/* packages at 4.0.0-alpha.10, upgrade to alpha.11 to ensure all packages are in sync.

4.0.0-alpha.10 (broken)

⚠️ Do not use this release. A sigstore transparency log conflict caused lerna publish to fail partway through. 18 packages were never published at this version while the rest were, resulting in a version mismatch across the monorepo. Use alpha.11 instead.

4.0.0-alpha.9

ESM fix now published for all packages

Five packages (@visx/vendor, @visx/point, @visx/scale, @visx/curve, @visx/mock-data) were stuck on pre-alpha.2 versions and still shipped ESM output without .js extensions or the esm/package.json "type": "module" marker. This release force-publishes every package so the alpha.2 ESM fix (#1976) is present across the board.

What you need to do: if you hit ERR_MODULE_NOT_FOUND in strict ESM environments (Vite SSR, Deno, edge runtimes) with any of those five packages, upgrading to this release resolves it.

4.0.0-alpha.8

@visx/responsive useParentSize external ref support

useParentSize now accepts an optional externalRef in its config object. If provided, the hook forwards the observed DOM node to both its internal state and the external ref, eliminating the need for a wrapper div when you already have a ref on the container element.

const myRef = useRef<HTMLDivElement>(null);
const { parentRef, width, height } = useParentSize({ externalRef: myRef });

return <div ref={parentRef}>...</div>;
// myRef.current is also set to the div element

Both RefObject and callback refs are supported.

What you need to do:

  • Most consumers: nothing β€” this is an additive, non-breaking change. Existing usage without an externalRef argument is unchanged.
  • If you were adding a wrapper div to combine your own ref with parentRef: you can now remove the wrapper and pass your ref directly via the externalRef option.

4.0.0-alpha.7

@visx/responsive ParentSize flex/grid infinite growth fix

ParentSize and withParentSize now render a two-div structure to prevent infinite height growth in flex and grid layouts (#881, #1014).

Previously, the component rendered a single wrapper <div style="width: 100%; height: 100%">. In flex or grid containers, a child SVG's intrinsic height could grow the wrapper, triggering ResizeObserver in a feedback loop.

The new structure uses an outer <div style="width: 100%; height: 100%; position: relative"> and an inner <div style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; overflow: hidden"> that holds the ResizeObserver and children. Since the inner div is absolutely positioned, children's intrinsic sizes cannot grow the outer container.

What you need to do:

  • Most consumers: nothing β€” the component API is unchanged and the visual result should be identical or better (no more infinite growth).
  • If you rely on the wrapper div's exact DOM structure (e.g., querying it in tests or applying CSS that targets a single wrapper div): the wrapper is now two nested divs. className, style, and all other HTML attributes still apply to the outer div.
  • If you use the deprecated parentSizeStyles prop: it still works and applies to the outer div, but position: relative is prepended to ensure the inner absolutely-positioned measurement div works correctly. If you explicitly set position in your custom styles, your value takes precedence.
  • If you use withParentSize: the same two-div structure applies. The container div is no longer a single <div style="width: 100%; height: 100%">.

4.0.0-alpha.6

@visx/responsive useParentSize callback ref fix

useParentSize now uses a callback ref internally instead of useRef. This fixes a bug where the hook could permanently report 0Γ—0 dimensions because parentRef.current was null when the useEffect first ran, and the dependency array never re-fired once the ref attached to the DOM (#1816).

The returned parentRef is now a callback ref ((node: T | null) => void) instead of a RefObject<T | null>. A new node property is also returned for direct access to the observed DOM element.

What you need to do:

  • Most consumers: nothing β€” <div ref={parentRef}> works with both RefObject and callback refs, and ParentSize component users are unaffected.

  • If you access parentRef.current directly: replace with the new node return value.

    - const { parentRef, width, height } = useParentSize();
    - console.log(parentRef.current);
    + const { parentRef, node, width, height } = useParentSize();
    + console.log(node);

4.0.0-alpha.5

@visx/xychart axis rendering fix (breaking)

BaseAxis no longer renders until at least one data series with non-empty data has been registered in DataContext. Previously, on initial render the axis could use the fallback scaleLinear() domain [0, 1], causing tickFormat to receive incorrect intermediate values before real data loaded (#1975).

This is a behavior change: axes that previously rendered immediately (showing 0–1 ticks while data was loading) will now render null until real data is available.

What you need to do:

  • Most consumers: nothing β€” this is a bugfix. If you were working around stale tick labels on first render, you can remove that workaround.
  • If you relied on the axis being visible before data loaded (e.g., to display a skeleton axis): render a placeholder <Axis /> from @visx/axis directly with your own scale until your data is ready, then switch to the xychart <Axis />.

Thank you wildseansy for the fix #1979

4.0.0-alpha.4

Internal only: replaced ts-node with tsx (esbuild-based) for faster TypeScript script execution. No consumer-facing changes.

4.0.0-alpha.3

@types/react and @types/react-dom as peer dependencies

React-based @visx/* packages now declare @types/react as a peer dependency instead of bundling their own copy. That way your app supplies a single version and you avoid duplicate or conflicting installs.

@visx/bounds, @visx/tooltip, @visx/xychart, and the @visx/visx meta-package all declare @types/react-dom as an optional peer directly (bounds and tooltip type DOM-touching APIs; xychart consumes tooltip and so transitively needs react-dom at runtime; the umbrella matches the pattern of its DOM-touching members).

@visx/brush and @visx/wordcloud also declare @types/react as an optional peer so consumers installing either package directly get the same type-dependency signal as the rest of the React-based @visx/* packages.

The peers are marked optional via peerDependenciesMeta, so non-TypeScript consumers will not see missing-peer warnings.

What you need to do:

  • React 18 or 19 (TypeScript users): add @types/react as a dev dependency matching your React version:

    yarn add -D @types/react

    If you use @visx/bounds, @visx/tooltip, @visx/xychart, or the @visx/visx meta-package, also install @types/react-dom:

    yarn add -D @types/react-dom

    Use the major versions that align with your react / react-dom versions.

  • Non-TypeScript consumers: no action needed.

If you see TypeScript errors about missing react types after upgrading, install the appropriate @types/react (and @types/react-dom when using @visx/bounds, @visx/tooltip, @visx/xychart, or @visx/visx) in your application.

4.0.0-alpha.2

Node ESM compatibility fix for published esm/ output

The published esm/ output now emits explicit .js extensions on relative imports and a nested esm/package.json with "type": "module". Strict Node ESM consumers (Vite/react-router SSR, Deno, edge runtimes) previously failed with ERR_MODULE_NOT_FOUND when resolving visx's ESM entry.

What you need to do: nothing β€” this is a bugfix. If you were pinned to 4.0.0-alpha.1 specifically to avoid a different ESM issue, you can upgrade safely.

4.0.0-alpha.1

Release-plumbing only: prerelease bump configuration and CI permissions for release PR comments. No consumer-facing changes.

4.0.0-alpha.0

The React 19 cut. This is the release that drops legacy React support and modernizes the build targets.

React 19 support (automatic JSX transform)

Every @visx/* package now uses the automatic JSX transform and imports React symbols as direct named imports or type-only imports (no more React.ReactNode namespace access). Peer dependency ranges were widened to ^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0.

What you need to do:

  • React 19 consumers: you should be able to upgrade with no code changes.
  • React 18 / 17 / 16.14+ consumers: still supported, no action needed.
  • React ≀ 16.13: no longer supported β€” upgrade React first.
  • If you import directly from deep subpaths (e.g. @visx/shape/lib/shapes/Bar), prefer the package root (@visx/shape). Deep imports are no longer the blessed API surface and may break in a future alpha. Every package now declares exports so Node's module resolver will honor the root entry.

Dropped IE11 support

Babel targets were bumped to modern browsers. IE11 and other legacy browsers are no longer supported.

What you need to do: if you still need IE11, stay on 3.x.

Package exports field

Every package now publishes an exports field mapping . to types, import, and require entries. This improves module resolution in modern bundlers and Node.js, but does restrict access to internal paths.

What you need to do: replace any deep imports (@visx/foo/lib/bar) with root imports (@visx/foo). If a symbol you need isn't re-exported from the root, open an issue.

Lerna v9 / OIDC publishing / resolution cleanup

Internal only β€” Lerna was upgraded to v9, publishing now uses OIDC trusted publishing, and unused ansi-* resolutions were removed from the root. No consumer impact.

@visx/demo (internal)

Upgraded to React 19, Next.js 15, and @react-spring/web v10. Affects contributors only.