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

@visx/event

@visx/event is a tiny browser utility for turning mouse, pointer, touch, or focus events into x and y coordinates usable by an SVG visualization. Its main localPoint function accepts an event alone or an explicit DOM element plus an event, accounts for an SVG screen transformation when available, and returns a @visx/point Point. The package also exports touchPoint as the explicit node-and-event form. It does not manage tooltips, gestures, selections, scales, or chart state.

Verdict

A sharp, minimal choice for SVG pointer coordinates inside a visx 4 chart. Skip it for HTML-only positioning or real gesture handling, and be deliberate about viewBox, nested-group, and portal coordinate spaces.

API stability5/5The public surface is only localPoint and touchPoint, while localPoint deliberately retains both event-only and node-plus-event signatures for backward compatibility. Mouse and touch support date back to early vx releases, and focus-event support was additive. Version 4 changed package entry points and React peers across visx, but the coordinate calls themselves remain small and familiar.
Docs3/5The package README clearly explains localPoint's return shape, null possibility, intended tooltip and nearest-datum uses, and both supported signatures with React examples. The source is short and typed enough to audit. Documentation does not explain that coordinates target the top-most owner SVG, how the bounding-box fallback interacts with viewBox scaling, why focus returns an element center, or how to position portals.
Maintenance5/5Version 4.0.0 was published on June 11, 2026 and the repository was pushed on June 22, 2026. The v4 migration guide is detailed about React support, synchronized package upgrades, exports maps, ESM output, modern browser targets, and broken alpha versions. The monorepo has 146 open issues and pull requests, but active stable and next release work is visible.
Ecosystem4/5The package receives 3,833,432 weekly downloads and belongs to the 20,999-star visx monorepo, where its coordinates feed custom tooltips, nearest-datum lookup, drag behavior, and low-level chart interaction. It works with native and React events and has first-party point types. Its role is intentionally narrow, so richer interactions require @visx/drag, @visx/tooltip, XYChart, or another gesture layer.

Use it if

  • You build custom SVG charts with visx and need pointer coordinates in the outer SVG's coordinate system
  • You need one helper that accepts native DOM events and React synthetic mouse, touch, or focus events
  • You want transformed SVG coordinates from getScreenCTM rather than raw page or viewport pixels
  • You already use other visx 4 packages and want a small tree-shakeable coordinate primitive with matching types
Skip it if

Setup reality

Install `@visx/event@^4` with React 18 or 19; React is a peer dependency even though this package exports functions rather than components. TypeScript declarations ship with the package, but `@types/react` is an optional peer and should match your React major in TypeScript projects. The sole runtime dependency is the exactly aligned `@visx/point` package. The visx 4 migration guide recommends upgrading every `@visx/*` package together because entry points, peer ranges, and internal versions changed as a set. Import only from `@visx/event`; v4's exports map blocks unsupported deep imports. There is no provider, CSS file, credential, native build, or configuration step. The subtle work is coordinate space. With an event from an SVG child, localPoint finds the top-level owner SVG and applies the inverse screen CTM, which handles a viewBox and CSS scaling. It does not return coordinates local to a transformed group. When the SVG transform path is unavailable, including common explicit-root cases, it falls back to client coordinates minus the element's bounding rectangle and client border. That fallback is CSS-pixel math and may not match viewBox units. The one-argument form depends on event.target; the two-argument form lets you pin the reference element when delegation or overlays make the target unstable. Always handle a null result. Touch uses changedTouches[0], so it is not a gesture engine, and preventing browser scroll during drawing usually also needs `touch-action: none`. Focus events have no pointer position, so the implementation uses the center of the focused target's bounding box. Finally, do not pass local SVG coordinates straight to a body-level tooltip portal without converting coordinate systems or using the tooltip package's container strategy.

Patterns

Read mouse coordinates inside an SVGtrack-mouse-position

import { localPoint } from '@visx/event';

function Overlay() {
  return (
    <rect
      width={600}
      height={300}
      fill="transparent"
      onMouseMove={(event) => {
        const point = localPoint(event);
        if (point) console.log(point.x, point.y);
      }}
    />
  );
}

The event-only form uses event.target and, for an SVG child, transforms viewport coordinates into the top-level owner's SVG coordinate system.

Anchor coordinates to an explicit SVGuse-explicit-svg-ref

import { useRef } from 'react';
import { localPoint } from '@visx/event';

function Chart() {
  const svgRef = useRef<SVGSVGElement>(null);
  return (
    <svg ref={svgRef} onMouseMove={(event) => {
      const svg = svgRef.current;
      if (!svg) return;
      const point = localPoint(svg, event);
      if (point) console.log(point);
    }}>
      {/* marks */}
    </svg>
  );
}

An explicit node avoids unstable event targets. For a scaled viewBox, verify the result because a root SVG can take the bounding-box fallback path.

Use one handler for mouse, pen, and touch pointershandle-pointer-events

function Mark() {
  return (
    <circle
      cx={80}
      cy={60}
      r={12}
      onPointerMove={(event) => {
        const point = localPoint(event);
        if (point) updateCursor(point);
      }}
    />
  );
}

Pointer events expose clientX and clientY and follow the mouse-event branch at runtime. Gesture state and pointer capture remain your responsibility.

Read the first changed touchhandle-touch-event

<svg
  style={{ touchAction: 'none' }}
  onTouchMove={(event) => {
    const point = localPoint(event);
    if (point) drawAt(point.x, point.y);
  }}
/>;

The implementation uses changedTouches[0]. touch-action: none prevents browser panning for a drawing surface, but this is not multi-touch tracking.

Call the explicit touchPoint exportuse-touch-point

import { touchPoint } from '@visx/event';

function onTouchMove(event: React.TouchEvent<SVGSVGElement>) {
  const svg = event.currentTarget;
  const point = touchPoint(svg, event);
  if (point) console.log(point.x, point.y);
}

touchPoint is the generic node-plus-event converter despite its name; localPoint is usually clearer because it supports both public signatures.

Position a tooltip from focusposition-keyboard-tooltip

<circle
  tabIndex={0}
  cx={x}
  cy={y}
  r={6}
  onFocus={(event) => {
    const point = localPoint(event);
    if (point) showTooltip({ left: point.x, top: point.y });
  }}
/>;

Focus events have no pointer coordinates, so localPoint uses the visual center of the focused target's bounding rectangle.

Convert pointer position back through a scalefind-nearest-datum

function onMouseMove(event: React.MouseEvent<SVGRectElement>) {
  const point = localPoint(event);
  if (!point) return;

  const domainX = xScale.invert(point.x);
  const nearest = findNearestDatum(data, domainX);
  setHovered(nearest);
}

invert is available on continuous scales, not band scales. For bands, compare point.x with band positions and bandwidth instead.

Use event-target conversion with a scaled viewBoxrespect-svg-viewbox

<svg viewBox="0 0 1000 500" width="500" height="250">
  <rect
    width={1000}
    height={500}
    fill="transparent"
    onMouseMove={(event) => {
      const point = localPoint(event);
      if (point) setCrosshair(point);
    }}
  />
</svg>;

Because the target is an SVG child, localPoint can invert the owner SVG's screen CTM and return viewBox user units instead of CSS pixels.

Convert into a transformed group's local spaceconvert-group-coordinates

function pointInGroup(group: SVGGElement, event: React.MouseEvent) {
  const ctm = group.getScreenCTM();
  if (!ctm) return null;
  const point = group.ownerSVGElement!.createSVGPoint();
  point.x = event.clientX;
  point.y = event.clientY;
  return point.matrixTransform(ctm.inverse());
}

localPoint targets the top-level owner SVG. Invert the group's own CTM when zoom, rotation, or nested transforms require group-local coordinates.

Guard a missing coordinate conversionhandle-null-result

const point = localPoint(event);
if (!point) {
  hideTooltip();
  return;
}
showTooltip({ left: point.x, top: point.y });

Do not silently replace null with {x: 0, y: 0}; that produces a visible jump to the chart origin when no valid target is available.

Place a tooltip inside the same chart containerplace-overlay-tooltip

const point = localPoint(event);
if (point) {
  setTooltip({
    datum,
    left: point.x + margin.left,
    top: point.y + margin.top,
  });
}

This works when the overlay shares the chart container's coordinate space. A body-level portal needs page or container conversion, not raw SVG-local values.

Coalesce high-frequency pointer updatesthrottle-pointer-updates

let frame = 0;
function onPointerMove(event: React.PointerEvent<SVGRectElement>) {
  const point = localPoint(event);
  if (!point) return;
  cancelAnimationFrame(frame);
  frame = requestAnimationFrame(() => setCursor(point));
}

localPoint is small, but React state updates and nearest-datum searches can dominate pointer-move cost. Cancel the pending frame during unmount in production code.

Alternatives

PackageRegistryPick it when
d3-selectionnpmYou already use D3 and want its pointer or pointers helpers, including multiple touch points
@use-gesture/reactnpmYou need stateful drag, pinch, wheel, hover, or move gestures rather than one coordinate conversion
@visx/dragnpmYou want visx drag components or hooks with start, move, end, delta, and reset state
@visx/tooltipnpmYour actual task is managing and rendering chart tooltips, including portal-aware placement