Skip to content

API Reference

The main hook. Attach it to a container element to enable pan, zoom, and pinch gestures.

import { useZoomPinch } from "use-zoom-pinch"
const result = useZoomPinch({
containerRef,
// ...options
})

OptionTypeDefaultDescription
containerRefRefObject<HTMLElement | null>requiredRef to the container element that receives gesture events
enabledbooleantrueEnable or disable all gesture handling
minScalenumber0.1Minimum allowed zoom level
maxScalenumber50Maximum allowed zoom level
panSpeednumber1Multiplier for pan speed (mouse wheel only)
zoomSpeednumber1Multiplier for zoom speed (mouse wheel only)

Trackpad detection: The hook uses a heuristic (deltaMode === 0 and fractional/small delta values) to distinguish trackpad from mouse wheel events. Mice with smooth scrolling (e.g. Logitech MX Master) may occasionally be misdetected as trackpad.

OptionTypeDefaultDescription
initialViewStateViewState{ x: 0, y: 0, zoom: 1 }Initial view for uncontrolled mode
viewStateViewStateControlled view state. When provided, the hook becomes controlled
onViewStateChange(view: ViewState) => voidCallback on every view change. Required for controlled mode
OptionTypeDefaultDescription
gesturesGesturesOptions{ pan: true, zoom: true, rotate: false }Toggle individual gesture types
panButton0 | 1 | 20Mouse button for pan drag (0=left, 1=middle, 2=right)
doubleTapDoubleTapOptions | false{ enabled: true, mode: "toggle", step: 2 }Double-tap/click zoom config. Pass false to disable
inertiaInertiaOptions | false{ enabled: true, friction: 0.92 }Pan momentum config. Pass false to disable
shouldHandleEvent(event: PointerEvent | WheelEvent | TouchEvent) => booleanFilter which events are handled. Return false to ignore
OptionTypeDefaultDescription
boundsBoundsOptionsConstrain panning within rectangular bounds
zoomSnapLevelsZoomSnapLevel[]Zoom snaps to nearest level on gesture end
snapToGridSnapToGridOptions | falsefalseSnap view position to a grid
OptionTypeDefaultDescription
keyboardKeyboardOptions | booleanfalseKeyboard navigation. Pass true for defaults

When keyboard is enabled, the hook automatically sets tabindex="0" on the container so it can receive focus. Key events are scoped to the focused container — multiple instances on a page work independently.

OptionTypeDefaultDescription
contentRect{ width: number; height: number }Content dimensions for fitToContent. Triggers auto-fit on mount + resize
OptionTypeDefaultDescription
rotationRotationOptionsMin/max angle and snap levels for rotation
wheelMode"pan" | "zoom""pan"Default wheel behavior. "zoom" inverts: wheel zooms, ctrl pans
cursorCursorOptions | falseenabledAutomatic cursor management (grab/grabbing). Pass false to disable
axis"x" | "y"Restrict gestures to a single axis
activationKeysActivationKeyOptionsRequire a key to be held for specific gesture types
OptionTypeDescription
onPanStart(view: ViewState) => voidFired when pointer drag starts
onPanEnd(view: ViewState) => voidFired when pointer drag ends (all pointers released)
onZoomStart(view: ViewState) => voidFired on first ctrl+wheel event
onZoomEnd(view: ViewState) => voidFired 150ms after last ctrl+wheel event (debounced)
onPinchStart(view: ViewState) => voidFired when multi-touch pinch begins
onPinchEnd(view: ViewState) => voidFired when fingers reduce below 2
onRotateStart(view: ViewState) => voidFired when rotation gesture begins (requires gestures.rotate: true)
onRotateEnd(view: ViewState) => voidFired when rotation gesture ends
onTransformEnd(view: ViewState) => voidFired after any gesture ends (pan, zoom, pinch, rotate)

Note: Event callbacks are only fired in response to user gestures, not imperative method calls (setView, panTo, etc.).


const {
view,
isAnimating,
setView,
centerZoom,
resetView,
zoomIn,
zoomOut,
zoomToElement,
panTo,
panBy,
zoomTo,
fitToRect,
rotateTo,
rotateBy,
screenToContent,
contentToScreen,
fitToContent,
snapZoom,
} = useZoomPinch({ containerRef })

Type: ViewState

Current view state. In uncontrolled mode, this is the hook’s internal state. In controlled mode, this is the viewState prop you passed in.

const { view } = useZoomPinch({ containerRef })
// view.x, view.y, view.zoom, view.rotation

Type: boolean

Whether an animation is currently running. Useful for disabling UI or showing loading states during transitions.

const { isAnimating } = useZoomPinch({ containerRef })
// true during animated transitions, false otherwise

Type: (view: ViewState, options?: AnimationOptions) => void

Imperatively set the view to an exact state.

// Instant
setView({ x: 100, y: 200, zoom: 2 })
// Animated
setView({ x: 100, y: 200, zoom: 2 }, { animate: true, duration: 500 })

Type: (targetZoom: number, options?: AnimationOptions) => void

Zoom to a target level, keeping the container center as the anchor point. The zoom is clamped to [minScale, maxScale].

// Zoom to 200%, centered
centerZoom(2, { animate: true })

Type: (options?: AnimationOptions) => void

Reset to the default view { x: 0, y: 0, zoom: 1 }.

resetView({ animate: true })

Type: (step?: number, options?: AnimationOptions) => void

Zoom in by a multiplicative step, centered on the container. Default step is 1.5.

zoomIn() // zoom *= 1.5
zoomIn(2) // zoom *= 2
zoomIn(1.5, { animate: true }) // animated

Type: (step?: number, options?: AnimationOptions) => void

Zoom out by a multiplicative step, centered on the container. Default step is 1.5.

zoomOut() // zoom /= 1.5
zoomOut(2) // zoom /= 2
zoomOut(1.5, { animate: true }) // animated

Type: (el: HTMLElement, scale?: number, options?: AnimationOptions) => void

Pan and zoom to center a specific element in the viewport. If scale is omitted, the current zoom level is preserved.

const el = document.getElementById("my-element")!
zoomToElement(el, 2, { animate: true }) // center and zoom to 2x
zoomToElement(el) // center at current zoom

Type: (x: number, y: number, options?: AnimationOptions) => void

Pan to center a content-space point in the viewport. The current zoom level is preserved.

// Center viewport on point (500, 300) in content space
panTo(500, 300, { animate: true })

Type: (dx: number, dy: number, options?: AnimationOptions) => void

Pan by a relative delta in screen pixels. Positive dx moves content right.

// Shift view 100px right, 50px down
panBy(100, 50, { animate: true })

Type: (zoom: number, point?: { x: number; y: number }, options?: AnimationOptions) => void

Zoom to a specific level. Without point, zooms centered on the container (same as centerZoom). With point, zooms centered on a content-space coordinate.

// Zoom to 3x, centered on container
zoomTo(3, undefined, { animate: true })
// Zoom to 2x, centered on point (100, 200) in content space
zoomTo(2, { x: 100, y: 200 }, { animate: true })

Type: (rect: { x: number; y: number; width: number; height: number }, options?: AnimationOptions & { padding?: number }) => void

Fit a content-space rectangle into the viewport. Calculates the optimal zoom level and centers the rectangle. The optional padding (in px) adds inset from the container edges.

// Fit a 400x300 region starting at (100, 50)
fitToRect({ x: 100, y: 50, width: 400, height: 300 }, { animate: true })
// With 20px padding from edges
fitToRect({ x: 0, y: 0, width: 800, height: 600 }, { animate: true, padding: 20 })

Type: (angle: number, options?: AnimationOptions) => void

Set the rotation to an absolute angle in degrees.

rotateTo(45, { animate: true }) // rotate to 45°
rotateTo(0, { animate: true }) // reset rotation

Type: (delta: number, options?: AnimationOptions) => void

Rotate by a relative delta in degrees. Positive values rotate clockwise.

rotateBy(90, { animate: true }) // rotate 90° clockwise
rotateBy(-45, { animate: true }) // rotate 45° counter-clockwise

Type: (screenX: number, screenY: number) => { x: number; y: number }

Convert screen-space coordinates to content-space. Useful for mapping click positions to content coordinates.

const contentPos = screenToContent(event.clientX, event.clientY)

Type: (contentX: number, contentY: number) => { x: number; y: number }

Convert content-space coordinates to screen-space. Useful for overlays and tooltips.

const screenPos = contentToScreen(100, 200)

Type: (options?: AnimationOptions & { padding?: number }) => void

Fit the content into the container. Requires the contentRect option. Automatically called on mount and container resize when contentRect is provided.

fitToContent({ animate: true, padding: 20 })

Type: (options?: AnimationOptions) => void

Snap the current zoom to the nearest level from zoomSnapLevels.

snapZoom({ animate: true })

// Hook
export { useZoomPinch } from "use-zoom-pinch"
// Easing functions
export { linear, easeOut, easeInOut } from "use-zoom-pinch"
// Types
export type {
ViewState,
UseZoomPinchOptions,
UseZoomPinchReturn,
AnimationOptions,
EasingFunction,
DoubleTapOptions,
InertiaOptions,
GesturesOptions,
BoundsOptions,
KeyboardOptions,
SnapToGridOptions,
ZoomSnapLevel,
RotationOptions,
CursorOptions,
ActivationKeyOptions,
} from "use-zoom-pinch"