Skip to main content

Picking

Type​

Component (Scene props) / Class (standalone)

Default Usage​

import { useRef } from 'react'
import { Scene } from 'react-three-lite'
import type { SceneComponents, PickEvent } from 'react-three-lite'
import * as THREE from 'three'

function App({ rendererType }: { rendererType?: 'webgpu' | 'webgl' }) {
const hoveredRef = useRef<THREE.Mesh | null>(null)

const handleCreated = (scene: THREE.Scene, components: SceneComponents) => {
const { camera } = components
if (!camera) return
camera.position.set(0, 1, 6)
camera.lookAt(0, 0, 0)

const geometry = new THREE.BoxGeometry(0.8, 0.8, 0.8)
const material = new THREE.MeshStandardMaterial({ color: 0x4fc3f7 })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
}

const handleClick = (event: PickEvent | null) => {
if (!event) return
const material = (event.object as THREE.Mesh).material as THREE.MeshStandardMaterial
material.color.setHex(Math.random() * 0xffffff)
}

// `onHover` only fires when the object under the pointer changes, so keep a
// reference to the previously hovered mesh in order to restore it.
const handleHover = (event: PickEvent | null) => {
const previous = hoveredRef.current
if (previous) {
const material = previous.material as THREE.MeshStandardMaterial
material.emissive.setHex(0x000000)
}

const mesh = event ? (event.object as THREE.Mesh) : null
if (mesh) {
const material = mesh.material as THREE.MeshStandardMaterial
material.emissive.setHex(0x444444)
}
hoveredRef.current = mesh
}

return (
<Scene
rendererType={rendererType}
onCreated={handleCreated}
onClick={handleClick}
onHover={handleHover}
bgColor="rgb(40, 42, 54)"
style={{ marginTop: '10px', width: '100%', height: '300px' }}
/>
)
}

Scene Props​

NameTypeDefaultDescription
onClick(event: PickEvent | null) => void—Fired on a click (pointer down + up within the threshold)
onHover(event: PickEvent | null) => void—Fired when the object under the pointer changes, or null when the pointer leaves the canvas
pickFilter(object: Object3D) => boolean—Optional predicate to filter which objects are pickable
pickRecursivebooleantrueTraverse children of the scene when picking

Picker Class​

For advanced use cases, the Picker class can be imported and used directly.

import { Picker } from 'react-three-lite'
import type { PickEvent, PickerOptions } from 'react-three-lite'
import * as THREE from 'three'

const picker = new Picker(scene, camera, renderer.domElement, {
recursive: true,
filter: (object) => object.name !== 'non-pickable',
clickThreshold: 5,
})

picker.on('click', (event: PickEvent | null) => {
if (event) console.log('clicked', event.object.name)
})

picker.on('hover', (event: PickEvent | null) => {
if (event) console.log('hovered', event.object.name)
})

// Cleanup
picker.dispose()

PickerOptions​

PropertyTypeDefaultDescription
targetsObject3D[]—Objects to test against, defaults to the whole scene
recursivebooleantrueTraverse descendants of the targets
filter(object: Object3D) => boolean—Keep only intersections that pass this predicate
enableHoverbooleantrueEmit hover events on pointer move
clickThresholdnumber5Max pointer travel (px) between down and up to count as a click
layersnumber | number[]—Camera layers to pick from, default: all layers
nearnumber—Raycaster near plane
farnumber—Raycaster far plane

PickEvent​

PropertyTypeDescription
objectObject3DThe intersected object
pointVector3Intersection point in world space
distancenumberDistance from the camera to the intersection point
intersectionIntersectionThe closest intersection that passed the filter
intersectionsIntersection[]All intersections that passed the filter, sorted by distance
pointerVector2Pointer position in normalized device coordinates
nativeEventPointerEvent | undefinedThe originating pointer event, absent for programmatic picks

Picker Methods​

NameParametersDescription
constructor(scene, camera, domElement, options?)Create a new Picker instance
on(type: 'click' | 'hover', listener) => () => voidSubscribe to an event, returns an unsubscribe function
off(type: 'click' | 'hover', listener) => voidUnsubscribe from an event
pickAt(clientX: number, clientY: number) => PickEvent | nullProgrammatically pick at a client position
setTargets(targets?: Object3D[]) => voidReplace the objects to test against
setFilter(filter?: (object) => boolean) => voidReplace the intersection filter
setEnabled(enabled: boolean) => voidEnable or disable the picker
getHovered() => Object3D | nullThe object currently under the pointer
dispose() => voidDetach listeners and release resources