API Reference
useZoomPinch(options)
Section titled “useZoomPinch(options)”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})Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
containerRef | RefObject<HTMLElement | null> | required | Ref to the container element that receives gesture events |
enabled | boolean | true | Enable or disable all gesture handling |
minScale | number | 0.1 | Minimum allowed zoom level |
maxScale | number | 50 | Maximum allowed zoom level |
panSpeed | number | 1 | Multiplier for pan speed (mouse wheel only) |
zoomSpeed | number | 1 | Multiplier for zoom speed (mouse wheel only) |
Trackpad detection: The hook uses a heuristic (
deltaMode === 0and 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.
State management
Section titled “State management”| Option | Type | Default | Description |
|---|---|---|---|
initialViewState | ViewState | { x: 0, y: 0, zoom: 1 } | Initial view for uncontrolled mode |
viewState | ViewState | — | Controlled view state. When provided, the hook becomes controlled |
onViewStateChange | (view: ViewState) => void | — | Callback on every view change. Required for controlled mode |
Gestures
Section titled “Gestures”| Option | Type | Default | Description |
|---|---|---|---|
gestures | GesturesOptions | { pan: true, zoom: true, rotate: false } | Toggle individual gesture types |
panButton | 0 | 1 | 2 | 0 | Mouse button for pan drag (0=left, 1=middle, 2=right) |
doubleTap | DoubleTapOptions | false | { enabled: true, mode: "toggle", step: 2 } | Double-tap/click zoom config. Pass false to disable |
inertia | InertiaOptions | false | { enabled: true, friction: 0.92 } | Pan momentum config. Pass false to disable |
shouldHandleEvent | (event: PointerEvent | WheelEvent | TouchEvent) => boolean | — | Filter which events are handled. Return false to ignore |
Constraints & snapping
Section titled “Constraints & snapping”| Option | Type | Default | Description |
|---|---|---|---|
bounds | BoundsOptions | — | Constrain panning within rectangular bounds |
zoomSnapLevels | ZoomSnapLevel[] | — | Zoom snaps to nearest level on gesture end |
snapToGrid | SnapToGridOptions | false | false | Snap view position to a grid |
Keyboard
Section titled “Keyboard”| Option | Type | Default | Description |
|---|---|---|---|
keyboard | KeyboardOptions | boolean | false | Keyboard 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.
Content fitting
Section titled “Content fitting”| Option | Type | Default | Description |
|---|---|---|---|
contentRect | { width: number; height: number } | — | Content dimensions for fitToContent. Triggers auto-fit on mount + resize |
Behavior
Section titled “Behavior”| Option | Type | Default | Description |
|---|---|---|---|
rotation | RotationOptions | — | Min/max angle and snap levels for rotation |
wheelMode | "pan" | "zoom" | "pan" | Default wheel behavior. "zoom" inverts: wheel zooms, ctrl pans |
cursor | CursorOptions | false | enabled | Automatic cursor management (grab/grabbing). Pass false to disable |
axis | "x" | "y" | — | Restrict gestures to a single axis |
activationKeys | ActivationKeyOptions | — | Require a key to be held for specific gesture types |
Event callbacks
Section titled “Event callbacks”| Option | Type | Description |
|---|---|---|
onPanStart | (view: ViewState) => void | Fired when pointer drag starts |
onPanEnd | (view: ViewState) => void | Fired when pointer drag ends (all pointers released) |
onZoomStart | (view: ViewState) => void | Fired on first ctrl+wheel event |
onZoomEnd | (view: ViewState) => void | Fired 150ms after last ctrl+wheel event (debounced) |
onPinchStart | (view: ViewState) => void | Fired when multi-touch pinch begins |
onPinchEnd | (view: ViewState) => void | Fired when fingers reduce below 2 |
onRotateStart | (view: ViewState) => void | Fired when rotation gesture begins (requires gestures.rotate: true) |
onRotateEnd | (view: ViewState) => void | Fired when rotation gesture ends |
onTransformEnd | (view: ViewState) => void | Fired 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.).
Return value
Section titled “Return value”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.rotationisAnimating
Section titled “isAnimating”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 otherwisesetView(view, options?)
Section titled “setView(view, options?)”Type: (view: ViewState, options?: AnimationOptions) => void
Imperatively set the view to an exact state.
// InstantsetView({ x: 100, y: 200, zoom: 2 })
// AnimatedsetView({ x: 100, y: 200, zoom: 2 }, { animate: true, duration: 500 })centerZoom(targetZoom, options?)
Section titled “centerZoom(targetZoom, options?)”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%, centeredcenterZoom(2, { animate: true })resetView(options?)
Section titled “resetView(options?)”Type: (options?: AnimationOptions) => void
Reset to the default view { x: 0, y: 0, zoom: 1 }.
resetView({ animate: true })zoomIn(step?, options?)
Section titled “zoomIn(step?, options?)”Type: (step?: number, options?: AnimationOptions) => void
Zoom in by a multiplicative step, centered on the container. Default step is 1.5.
zoomIn() // zoom *= 1.5zoomIn(2) // zoom *= 2zoomIn(1.5, { animate: true }) // animatedzoomOut(step?, options?)
Section titled “zoomOut(step?, options?)”Type: (step?: number, options?: AnimationOptions) => void
Zoom out by a multiplicative step, centered on the container. Default step is 1.5.
zoomOut() // zoom /= 1.5zoomOut(2) // zoom /= 2zoomOut(1.5, { animate: true }) // animatedzoomToElement(el, scale?, options?)
Section titled “zoomToElement(el, scale?, options?)”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 2xzoomToElement(el) // center at current zoompanTo(x, y, options?)
Section titled “panTo(x, y, options?)”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 spacepanTo(500, 300, { animate: true })panBy(dx, dy, options?)
Section titled “panBy(dx, dy, options?)”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 downpanBy(100, 50, { animate: true })zoomTo(zoom, point?, options?)
Section titled “zoomTo(zoom, point?, options?)”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 containerzoomTo(3, undefined, { animate: true })
// Zoom to 2x, centered on point (100, 200) in content spacezoomTo(2, { x: 100, y: 200 }, { animate: true })fitToRect(rect, options?)
Section titled “fitToRect(rect, options?)”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 edgesfitToRect({ x: 0, y: 0, width: 800, height: 600 }, { animate: true, padding: 20 })rotateTo(angle, options?)
Section titled “rotateTo(angle, options?)”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 rotationrotateBy(delta, options?)
Section titled “rotateBy(delta, options?)”Type: (delta: number, options?: AnimationOptions) => void
Rotate by a relative delta in degrees. Positive values rotate clockwise.
rotateBy(90, { animate: true }) // rotate 90° clockwiserotateBy(-45, { animate: true }) // rotate 45° counter-clockwisescreenToContent(screenX, screenY)
Section titled “screenToContent(screenX, screenY)”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)contentToScreen(contentX, contentY)
Section titled “contentToScreen(contentX, contentY)”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)fitToContent(options?)
Section titled “fitToContent(options?)”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 })snapZoom(options?)
Section titled “snapZoom(options?)”Type: (options?: AnimationOptions) => void
Snap the current zoom to the nearest level from zoomSnapLevels.
snapZoom({ animate: true })Exports
Section titled “Exports”// Hookexport { useZoomPinch } from "use-zoom-pinch"
// Easing functionsexport { linear, easeOut, easeInOut } from "use-zoom-pinch"
// Typesexport type { ViewState, UseZoomPinchOptions, UseZoomPinchReturn, AnimationOptions, EasingFunction, DoubleTapOptions, InertiaOptions, GesturesOptions, BoundsOptions, KeyboardOptions, SnapToGridOptions, ZoomSnapLevel, RotationOptions, CursorOptions, ActivationKeyOptions,} from "use-zoom-pinch"