This document tracks consumer-facing changes for each 4.0.0-alpha.* release. Upgrades are
cumulative — if you're jumping several versions, apply the steps from each section in order.
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.
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.
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.
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 elementBoth 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
externalRefargument 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 theexternalRefoption.
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
parentSizeStylesprop: it still works and applies to the outer div, butposition: relativeis prepended to ensure the inner absolutely-positioned measurement div works correctly. If you explicitly setpositionin 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%">.
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 bothRefObjectand callback refs, andParentSizecomponent users are unaffected. -
If you access
parentRef.currentdirectly: replace with the newnodereturn value.- const { parentRef, width, height } = useParentSize(); - console.log(parentRef.current); + const { parentRef, node, width, height } = useParentSize(); + console.log(node);
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/axisdirectly with your own scale until your data is ready, then switch to the xychart<Axis />.
Thank you wildseansy for the fix #1979
Internal only: replaced ts-node with tsx (esbuild-based) for faster TypeScript script execution.
No consumer-facing changes.
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 and React
19+ consumers who rely on React's built-in types will not see missing-peer warnings.
What you need to do:
-
React 18 and earlier (TypeScript users): add
@types/reactas a dev dependency matching your React version:yarn add -D @types/react
If you use
@visx/bounds,@visx/tooltip,@visx/xychart, or the@visx/visxmeta-package, also install@types/react-dom:yarn add -D @types/react-dom
Use the major versions that align with your
react/react-domversions. -
React 19+: rely on the types shipped with
reactandreact-dom. The optional peer means no install warning either way. -
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.
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.
Release-plumbing only: prerelease bump configuration and CI permissions for release PR comments. No consumer-facing changes.
The React 19 cut. This is the release that drops legacy React support and modernizes the build targets.
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 declaresexportsso Node's module resolver will honor the root entry.
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.
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.
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.
Upgraded to React 19, Next.js 15, and @react-spring/web v10. Affects contributors only.