Skip to main content
Zoom Image provides four distinct zoom modes, each designed for specific use cases and user interactions. All modes are built on the same headless core architecture but expose different interaction patterns.

Overview

The library provides these zoom functions:
  • createZoomImageWheel - Zoom with mouse wheel or pinch gestures
  • createZoomImageHover - Magnifying glass effect on hover
  • createZoomImageMove - Follow cursor with zoomed image
  • createZoomImageClick - Click to zoom, then move
Each function is available from the @zoom-image/core package and returns a consistent API with cleanup(), subscribe(), setState(), and getState() methods.

Wheel/Pinch Mode

The wheel/pinch mode allows users to zoom in and out using mouse wheel scrolling or pinch gestures on touch devices. This mode is ideal for detailed image exploration and supports advanced features like rotation and pan.

When to Use

  • Product galleries where users need to examine details
  • Maps or diagrams requiring precise zooming
  • Photo viewers with touch and mouse support
  • Any interface requiring smooth, progressive zoom control

Configuration

import { createZoomImageWheel } from "@zoom-image/core"
import type { ZoomImageWheelOptions } from "@zoom-image/core"

const container = document.getElementById("zoom-container")

const options: ZoomImageWheelOptions = {
  maxZoom: 4,                      // Maximum zoom level (default: 4)
  wheelZoomRatio: 0.1,             // Zoom speed multiplier (default: 0.1)
  dblTapAnimationDuration: 300,    // Double-tap animation duration in ms
  shouldZoomOnSingleTouch: () => true,  // Enable single-touch zoom
  zoomTarget: null,                // Custom zoom target element
  initialState: {
    currentZoom: 1,
    enable: true,
    currentRotation: 0
  }
}

const zoomImage = createZoomImageWheel(container, options)

State Shape

The wheel/pinch mode maintains the following state:
type ZoomImageWheelState = {
  currentRotation: number    // Rotation angle in degrees
  currentZoom: number        // Current zoom level (1 = no zoom)
  enable: boolean            // Whether zoom is enabled
  currentPositionX: number   // X-axis translation in pixels
  currentPositionY: number   // Y-axis translation in pixels
}

Features

  • Mouse wheel and touchpad scrolling
  • Two-finger pinch gestures on touch devices
  • Double-tap to zoom in/out with animation
  • Pan while zoomed (single touch or mouse drag)
  • Rotation support
  • Zoom centers on cursor/touch position

Example

const container = document.getElementById("image-container")
const zoomImage = createZoomImageWheel(container, {
  maxZoom: 5,
  wheelZoomRatio: 0.15
})

// Subscribe to state changes
zoomImage.subscribe(({ state }) => {
  console.log("Current zoom:", state.currentZoom)
  console.log("Position:", state.currentPositionX, state.currentPositionY)
})

// Programmatically control zoom
zoomImage.setState({ currentZoom: 2 })
zoomImage.setState({ currentRotation: 90 })

// Cleanup when done
zoomImage.cleanup()

Hover Mode

Hover mode creates a magnifying glass effect, displaying a zoomed portion of the image in a separate target element as the user moves their cursor over the source image.

When to Use

  • E-commerce product pages with detail views
  • Medical or scientific imagery requiring inspection
  • Art galleries or museum collections
  • Any interface where screen space allows for a dedicated zoom viewport

Configuration

import { createZoomImageHover } from "@zoom-image/core"
import type { ZoomImageHoverOptions } from "@zoom-image/core"

const container = document.getElementById("image-container")
const zoomTarget = document.getElementById("zoom-target")

const options: ZoomImageHoverOptions = {
  customZoom: { width: 400, height: 400 },  // Zoom viewport size (required)
  zoomTarget: zoomTarget,                    // Where to render zoomed image (required)
  scale: 2,                                  // Zoom scale factor (default: 2)
  zoomImageSource: "path/to/high-res.jpg",  // Optional high-res image source
  zoomLensClass: "custom-lens",             // CSS class for lens element
  zoomTargetClass: "zoom-active",           // CSS class when zoom is active
  zoomLensScale: 1,                          // Lens size multiplier (default: 1)
  disableScrollLock: false                   // Keep scroll enabled while zooming
}

const zoomImage = createZoomImageHover(container, options)

State Shape

type ZoomImageHoverState = {
  zoomedImgStatus: "idle" | "loading" | "loaded" | "error"  // Image loading status
  enabled: boolean                                          // Whether hover zoom is enabled
}

Features

  • Separate zoom target for magnified view
  • Optional high-resolution image source
  • Customizable lens appearance and size
  • Automatic scroll locking during zoom
  • Loading states for async image loading

Example

const container = document.getElementById("product-image")
const zoomTarget = document.getElementById("zoom-viewer")

const zoomImage = createZoomImageHover(container, {
  customZoom: { width: 500, height: 500 },
  zoomTarget: zoomTarget,
  scale: 3,
  zoomImageSource: "/images/product-high-res.jpg",
  zoomLensClass: "product-lens"
})

// Monitor loading status
zoomImage.subscribe(({ state }) => {
  if (state.zoomedImgStatus === "loading") {
    console.log("Loading high-res image...")
  } else if (state.zoomedImgStatus === "loaded") {
    console.log("High-res image ready")
  }
})

// Disable hover zoom temporarily
zoomImage.setState({ enabled: false })

Move Mode

Move mode displays a zoomed version of the image that follows the cursor position. The zoomed image appears directly over the container.

When to Use

  • Mobile-friendly image viewers
  • Simple product image zoom without separate viewport
  • Touch-enabled galleries
  • Interfaces where space for a separate zoom target is limited

Configuration

import { createZoomImageMove } from "@zoom-image/core"
import type { ZoomImageMoveOptions } from "@zoom-image/core"

const container = document.getElementById("image-container")

const options: ZoomImageMoveOptions = {
  zoomFactor: 4,                             // Zoom magnification (default: 4)
  zoomImageSource: "path/to/high-res.jpg",  // Optional high-res image
  disableScrollLock: false,                  // Deprecated - kept for compatibility
  disabledContextMenu: false,                // Disable right-click context menu
  zoomImageProps: {
    alt: "Zoomed product image",
    className: "zoomed-image"
  }
}

const zoomImage = createZoomImageMove(container, options)

State Shape

type ZoomImageMoveState = {
  zoomedImgStatus: "idle" | "loading" | "loaded" | "error"  // Image loading status
}

Features

  • Zoomed image follows cursor/touch position
  • Single pointer tracking (first touch/pointer wins)
  • Automatic scroll disabling on touch devices
  • Optional context menu prevention for touch devices
  • Bounds checking to keep zoom within container

Example

const container = document.getElementById("gallery-image")

const zoomImage = createZoomImageMove(container, {
  zoomFactor: 3,
  disabledContextMenu: true,  // Prevent long-press menu on mobile
  zoomImageProps: {
    alt: "Magnified view",
    className: "zoom-overlay"
  }
})

// Monitor loading state
zoomImage.subscribe(({ state }) => {
  console.log("Image status:", state.zoomedImgStatus)
})

// Cleanup when component unmounts
zoomImage.cleanup()

Click Mode

Click mode requires users to click to activate zoom, then move their cursor to pan the zoomed image. Clicking again deactivates the zoom.

When to Use

  • Interfaces where hover interactions are used for other purposes
  • Mobile-first designs (tap to zoom)
  • When explicit user intent is required before zooming
  • Gallery lightboxes or modal viewers

Configuration

import { createZoomImageClick } from "@zoom-image/core"
import type { ZoomImageClickOptions } from "@zoom-image/core"

const container = document.getElementById("image-container")

const options: ZoomImageClickOptions = {
  zoomFactor: 4,                             // Zoom magnification (default: 4)
  zoomImageSource: "path/to/high-res.jpg",  // Optional high-res image
  disableScrollLock: false,                  // Keep scroll enabled (default: false)
  zoomImageProps: {
    alt: "Zoomed image"
  }
}

const zoomImage = createZoomImageClick(container, options)

State Shape

type ZoomImageClickState = {
  zoomedImgStatus: "idle" | "loading" | "loaded" | "error"  // Image loading status
}

Features

  • Click/tap to activate zoom mode
  • Move cursor/touch to pan while zoomed
  • Click/tap again to deactivate
  • Optional scroll lock control
  • Bounds checking for pan limits

Example

const container = document.getElementById("artwork")

const zoomImage = createZoomImageClick(container, {
  zoomFactor: 5,
  disableScrollLock: false,
  zoomImageProps: {
    alt: "High resolution artwork detail"
  }
})

// Track zoom activation
let isZooming = false
container.addEventListener("pointerdown", () => {
  isZooming = !isZooming
  console.log(isZooming ? "Zoom activated" : "Zoom deactivated")
})

// Cleanup
zoomImage.cleanup()

Common Patterns

Cleanup and Memory Management

All zoom modes return a cleanup function that should be called when the zoom instance is no longer needed:
const zoomImage = createZoomImageWheel(container, options)

// Later, when component unmounts or container is removed:
zoomImage.cleanup()

State Subscription

Subscribe to state changes to sync with your UI framework:
const unsubscribe = zoomImage.subscribe(({ state, prevState }) => {
  console.log("State changed from", prevState, "to", state)
})

// Unsubscribe when no longer needed
unsubscribe()

Programmatic Control

All modes except move and click support programmatic state updates:
// Wheel mode
zoomImage.setState({ 
  currentZoom: 3,
  currentRotation: 90,
  enable: true 
})

// Hover mode
zoomImage.setState({ 
  enabled: false 
})

High-Resolution Images

For better image quality, provide a separate high-resolution image source:
const zoomImage = createZoomImageHover(container, {
  // ... other options
  zoomImageSource: "/images/[email protected]"  // High-res version
})
The library automatically handles loading states while the high-res image loads.

Choosing the Right Mode

ModeBest ForInteractionMobile Support
Wheel/PinchDetailed exploration, maps, technical diagramsScroll/pinch + panExcellent
HoverE-commerce, separate zoom viewportHover to activateDesktop only
MoveSimple zoom, limited spaceHover/touch to activate and followGood
ClickExplicit user control, mobile-firstClick/tap to toggle, move to panExcellent
All zoom modes work with the container’s img element by default. Make sure your container has exactly one <img> element as a child.
Remember to call cleanup() when removing zoom instances to prevent memory leaks and remove event listeners.

Build docs developers (and LLMs) love