@visx/point
@visx/point is a dependency-free two-dimensional coordinate container from the visx visualization monorepo. It exports a mutable Point class with public numeric x and y fields, methods that copy those coordinates into an object or array, and two standalone helpers that add or subtract points. Despite the visx name, the package contains no React, SVG, DOM, scale, or chart code. It is a tiny shared data shape used around visualization calculations, not a vector-math or geometry library.
Useful as a shared visx coordinate type, but too small to justify a new dependency in most standalone applications. If two public numbers and addition are not enough, skip past it to a real vector or geometry library.
Use it if
- You already use visx packages and want the same Point value type expected by nearby visualization code
- You need a minimal mutable x/y class plus object and array conversion without any runtime dependencies
- You want named sumPoints and subtractPoints helpers that always return a new Point
- You publish code for both ESM and CommonJS consumers and value the package's explicit exports, TypeScript declarations, and sideEffects false marker
- You only need to pass coordinates between your own functions: a TypeScript type such as { x: number; y: number } and two arithmetic lines avoid a package whose public implementation is similarly small
- You need actual vector math: there is no length, distance, dot product, normalization, angle, rotation, scaling, interpolation, matrix transform, equality, or bounds API; use victor or gl-matrix
- You require immutable values: x and y are public writable fields, while only sumPoints and subtractPoints guarantee a new result
- You parse user or network data: the constructor accepts NaN, Infinity, strings at JavaScript runtime, and any object-shaped values without validation, even though TypeScript declares optional numbers
- You call new Point() with no argument expecting the origin: x and y default to zero only after the constructor successfully destructures an options object, so new Point() throws while new Point({}) works
- You need discoverable documentation: the package README documents Point, value(), and toArray(), but omits the exported sumPoints and subtractPoints helpers and says nothing about mutability, invalid numbers, or the required constructor object
- You need three-dimensional coordinates, spatial indexes, paths, polygons, collision tests, or computational geometry: this package represents exactly two scalar fields
Setup reality
Installation is the whole build setup: @visx/point 4.0.0 has no dependencies, peer dependencies, native code, styles, configuration, React import, or browser requirement. The wider visx v4 suite requires React 18 or 19, but this package's own manifest does not, and its source is ordinary arithmetic, so it can be used in Node or non-React browser code. It publishes an ESM build, a CommonJS build, TypeScript declarations, a root exports map, and sideEffects: false. Import Point, sumPoints, and subtractPoints from the package root rather than reaching into lib or esm internals. The main first-run surprise is constructor shape: you must pass an object. new Point({}) creates (0, 0), new Point({ x: 4 }) creates (4, 0), but new Point() throws because the argument itself has no default. TypeScript prevents that call when checked, yet plain JavaScript will encounter it at runtime. Coordinates have no units, coordinate-system direction, precision policy, or finite-number validation. Public x and y fields are mutable. value() returns a fresh { x, y } snapshot and toArray() returns a fresh two-element array, so mutating either returned value does not update the Point. sumPoints(a, b) and subtractPoints(a, b) also allocate new Point objects and leave inputs alone. Their TypeScript signatures require Point instances, though the JavaScript implementation only reads x and y. If this crosses an API boundary, validate Number.isFinite yourself. If you are not already using visx, consider whether a local Point type is clearer than explaining why a three-method coordinate class is a production dependency.
Patterns
Create a two-dimensional pointcreate-point
import { Point } from '@visx/point';
const point = new Point({ x: 24, y: 80 });
console.log(point.x, point.y);
Coordinates are public mutable numbers. The class does not validate finiteness, units, or coordinate-system direction.
Create the origin with an empty options objectcreate-origin
import { Point } from '@visx/point';
const origin = new Point({});
console.log(origin.value()); // { x: 0, y: 0 }
Pass the object. new Point() throws at runtime because the constructor destructures its argument before applying x and y defaults.
Default a missing coordinate to zerodefault-one-coordinate
const xOnly = new Point({ x: 12 });
const yOnly = new Point({ y: -4 });
console.log(xOnly.toArray()); // [12, 0]
console.log(yOnly.toArray()); // [0, -4]
Only omitted x or y receives zero. Explicit NaN, Infinity, null, or a runtime string is not cleaned or rejected.
Validate external coordinates before constructionvalidate-coordinates
function pointFromJson(input) {
if (!input || !Number.isFinite(input.x) || !Number.isFinite(input.y)) {
throw new TypeError('finite x and y required');
}
return new Point({ x: input.x, y: input.y });
}
TypeScript declarations help at compile time, but Point performs no runtime validation for parsed JSON or other untyped inputs.
Copy coordinates into a plain objectcopy-point-value
const point = new Point({ x: 3, y: 5 });
const snapshot = point.value();
snapshot.x = 99;
console.log(point.x); // 3
value() allocates a new object. Mutating that snapshot does not mutate the Point instance.
Convert coordinates to an arrayconvert-to-tuple
const point = new Point({ x: 3, y: 5 });
const [x, y] = point.toArray();
const svgTranslate = `translate(${x} ${y})`;
toArray() returns a number array, not a readonly fixed-length TypeScript tuple, and allocates a fresh array on every call.
Update a point in placemutate-point
const cursor = new Point({ x: 0, y: 0 });
cursor.x = pointerEvent.clientX;
cursor.y = pointerEvent.clientY;
The fields are intentionally writable. Avoid sharing a mutable Point where consumers assume coordinate values are stable.
Translate a point by an offsetadd-points
import { Point, sumPoints } from '@visx/point';
const position = new Point({ x: 40, y: 20 });
const margin = new Point({ x: 16, y: 12 });
const screenPosition = sumPoints(position, margin);
sumPoints returns a new Point and does not mutate either input.
Compute a drag deltasubtract-points
import { Point, subtractPoints } from '@visx/point';
const start = new Point({ x: 100, y: 80 });
const current = new Point({ x: 128, y: 65 });
const delta = subtractPoints(current, start);
console.log(delta.value()); // { x: 28, y: -15 }
Order matters: subtractPoints(a, b) computes a minus b for both coordinates.
Calculate a midpointfind-midpoint
import { Point, sumPoints } from '@visx/point';
function midpoint(a, b) {
const total = sumPoints(a, b);
return new Point({ x: total.x / 2, y: total.y / 2 });
}
Scaling and division are not built in. If manual vector operations spread across the codebase, use a fuller vector library.
Average a list of pointsfind-centroid
import { Point, sumPoints } from '@visx/point';
function centroid(points) {
if (points.length === 0) return undefined;
const total = points.reduce(
(sum, point) => sumPoints(sum, point),
new Point({}),
);
return new Point({ x: total.x / points.length, y: total.y / points.length });
}
Each sumPoints call allocates. For large arrays or animation loops, accumulate numeric x and y totals directly.
Serialize points for an SVG polylinebuild-svg-polyline
const points = [
new Point({ x: 0, y: 10 }),
new Point({ x: 20, y: 4 }),
new Point({ x: 40, y: 18 }),
];
const attribute = points.map((point) => point.toArray().join(',')).join(' ');
// <polyline points={attribute} />
This package does not render SVG. It only supplies coordinates; React or DOM code owns the element, keys, scales, and clipping.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| gl-matrix | npm | Choose it for allocation-conscious vec2, vec3, matrices, quaternions, transforms, and numeric rendering work |
| victor | npm | Choose it for a fluent two-dimensional vector class with length, angle, normalize, rotate, dot, distance, and interpolation operations |
| @flatten-js/core | npm | Choose it when points are only the start and you also need lines, circles, arcs, polygons, intersections, and spatial relations |