# @pmndrs/pointer-events

> framework agnostic pointer-events implementation for threejs

Latest version **6.6.30** (published 2026-05-29) · SEE LICENSE IN LICENSE license · 0 weekly downloads

## Install

```sh
npm install @pmndrs/pointer-events
pnpm add @pmndrs/pointer-events
yarn add @pmndrs/pointer-events
bun add @pmndrs/pointer-events
```

## Health

**Score 65/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 6.6.30 |
| Published | 2026-05-29 |
| First published | 2024-07-16 |
| Weekly downloads | 0 |
| License | SEE LICENSE IN LICENSE |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 106.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 2608 |
| Author | Bela Bohlender |
| Maintainers | abernier, pmndrs01, drcmda, bjornstar, dennissmolek, bela-bohlender, isaacmason, krispyaa |
| Keywords | r3f, events, pointer, three.js, userinterface, typescript |

## Links

- npm: https://www.npmjs.com/package/@pmndrs/pointer-events
- Repository: https://github.com/pmndrs/xr
- Issues: https://github.com/pmndrs/xr/issues
- npm.io page: https://npm.io/package/@pmndrs/pointer-events

## Alternatives

- [@openai/codex-sdk](https://npm.io/package/@openai/codex-sdk.md) — 731.4K weekly downloads
- [babel-plugin-transform-react-jsx](https://npm.io/package/babel-plugin-transform-react-jsx.md) — 565.0K weekly downloads
- [babel-helper-remove-or-void](https://npm.io/package/babel-helper-remove-or-void.md) — 508.5K weekly downloads
- [@pnpm/store-controller-types](https://npm.io/package/@pnpm/store-controller-types.md) — 186.9K weekly downloads
- [react-native-signature-canvas](https://npm.io/package/react-native-signature-canvas.md) — 155.6K weekly downloads

## Recent versions

- 6.6.30 (latest) — 2026-05-29
- 6.5.1-alpha.2 (alpha) — 2025-01-21
- 6.6.29 — 2026-01-06
- 6.6.28 — 2025-11-20
- 6.6.27 — 2025-10-05
- 6.6.26 — 2025-09-09
- 6.6.25 — 2025-08-19
- 6.6.24 — 2025-08-19
- 6.6.23 — 2025-08-19
- 6.6.22 — 2025-08-05
- 6.6.21 — 2025-08-05
- 6.6.20 — 2025-07-19
- 6.6.19 — 2025-07-09
- 6.6.18 — 2025-07-08
- 6.6.17 — 2025-05-17
- … 68 more at https://npm.io/package/@pmndrs/pointer-events/versions

## README

# pointer-events

_framework agnostic pointer-events implementation for three.js_

based on [🎯 Designing Pointer-events for 3D & XR](https://polar.sh/bbohlender/posts/designing-pointer-events-for-3d)

## How to use

```js
import * as THREE from 'three'
import { forwardHtmlEvents } from '@pmndrs/pointer-events'

const canvas = document.getElementById('canvas')
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(70, width / height, 0.01, 10)
camera.position.z = 1
const { update } = forwardHtmlEvents(canvas, () => camera, scene)

const width = window.innerWidth,
  height = window.innerHeight

const geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2)
const material = new THREE.MeshBasicMaterial({ color: new THREE.Color('red') })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)

mesh.addEventListener('pointerover', () => material.color.set('blue'))
mesh.addEventListener('pointerout', () => material.color.set('red'))

const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)
renderer.setAnimationLoop(() => {
  update()
  renderer.render(scene, camera)
})
```

## Filtering

Based on the css `pointer-events` property, the behavior of pointer events can be configured with the values `none`, `listener`, or `auto`.

```js
object.pointerEvents = 'none'
```

The values `none` and `auto` correspond to the css properties, where `none` means that an object is not directly targetted and `auto` means the object is always targetted for events. The additional value `listener`, which is the default value, expresses that the object is only targetted by events if the object has any listeners. In 3D scenes this default is more reasonable than `auto`, which is the default in the web, because 3D scenes often contain semi-transparent content, such as particles, that should not catch pointer events by default.

In addition to the `pointerEvents` property, each 3D object can also filter events based on the `pointerType` with the `pointerEventsType` property. This property defaults to the value `all`, which expresses that pointer events from pointers of all types should be accepted. To filter specific pointer types, such as `screen-mouse`, which represents a normal mouse used through a 2D screen, `pointerEventsType` can be set to `{ allow: "screen-mouse" }` or `{ deny: "screen-touch" }`. `pointerEventsType`'s `allow` and `deny` accept strings and array of strings. In case more custom logic is needed, `pointerEventsType` also accepts a function. In general the pointer types `screen-touch`, `screen-pen`, `ray`, `grab`, and `touch` are used by default. For pointer events that were forwarded through a portal using `forwardObjectEvents`, their `pointerType` is prefixed with `forward-`, while events forwarded from the dom to the scene are prefixed with `screen-`.

## But wait ... there's more

Create your own `Pointer` that can represent a WebXR controller or something else. These `Pointer` can use a normal `Ray` for intersection, or a set of `Lines`, or even a `Sphere`, for grab and touch events.

## Performance

In some cases multi-modal interactivity requires multiple pointers at the same time. Executing `pointer.move`, such as in the following example, can lead to performance issues because the scene graph will be traversed several times.

```ts
leftGrabPointer.move()
leftTouchPointer.move()
leftRayPointer.move()
rightGrabPointer.move()
rightTouchPointer.move()
rightRayPointer.move()
```

In this case, performance can be improved by combining the pointer using `CombinedPointer`, which will traverse the scene graph once per combined pointer, calculating the intersections for each pointer on each object.

```ts
const leftPointer = new CombinedPointer()
const rightPointer = new CombinedPointer()
leftPointer.register(leftGrabPointer)
leftPointer.register(leftTouchPointer)
leftPointer.register(leftRayPointer)
rightPointer.register(rightGrabPointer)
rightPointer.register(rightTouchPointer)
rightPointer.register(rightRayPointer)

leftPointer.move()
rightPointer.move()
```

## Pitfalls

The `pointerEvents` attribute of any Mesh/Object3D/... will not be cloned when cloning the object.

---
_Source: https://npm.io/package/@pmndrs/pointer-events · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
