Skip to main content
This guide shows you how to implement all zoom modes using vanilla JavaScript. The Zoom Image library is framework-agnostic and works seamlessly with plain HTML, CSS, and JavaScript.

Installation

First, install the core package:
npm install @zoom-image/core

Wheel Zoom Mode

Wheel zoom allows users to zoom in and out using the mouse wheel or pinch gestures on touch devices.

HTML Structure

<div id="image-wheel-container" class="h-[300px] w-[200px] cursor-crosshair">
  <img class="h-full w-full" alt="Product Image" src="/product.jpg" />
</div>

<div class="controls">
  <button id="zoomInBtn">Zoom In</button>
  <button id="zoomOutBtn">Zoom Out</button>
  <button id="rotateBtn">Rotate</button>
  <button id="cropBtn">Crop Image</button>
  <p id="currentZoom">Current zoom: 100%</p>
</div>

<img id="croppedImage" class="h-[300px] w-[200px]" hidden />

JavaScript Implementation

import { createZoomImageWheel, cropImage } from "@zoom-image/core";

const container = document.getElementById("image-wheel-container");
const result = createZoomImageWheel(container, {
  maxZoom: 4,
  wheelZoomRatio: 0.1,
});

// Subscribe to state changes
const unsubscribe = result.subscribe(({ state }) => {
  const currentZoom = document.getElementById("currentZoom");
  currentZoom.textContent = `Current zoom: ${Math.round(state.currentZoom * 100)}%`;
});

// Programmatic zoom controls
const zoomInBtn = document.getElementById("zoomInBtn");
zoomInBtn.addEventListener("click", () => {
  result.setState({
    currentZoom: result.getState().currentZoom + 0.5,
  });
});

const zoomOutBtn = document.getElementById("zoomOutBtn");
zoomOutBtn.addEventListener("click", () => {
  result.setState({
    currentZoom: result.getState().currentZoom - 0.5,
  });
});

// Rotation control
const rotateBtn = document.getElementById("rotateBtn");
rotateBtn.addEventListener("click", () => {
  result.setState({
    currentRotation: result.getState().currentRotation + 90,
  });
});

// Crop functionality
const cropBtn = document.getElementById("cropBtn");
cropBtn.addEventListener("click", async () => {
  const currentState = result.getState();
  const croppedImageUrl = await cropImage({
    image: container.querySelector("img"),
    currentZoom: currentState.currentZoom,
    positionX: currentState.currentPositionX,
    positionY: currentState.currentPositionY,
    rotation: currentState.currentRotation,
  });

  const croppedImage = document.getElementById("croppedImage");
  croppedImage.src = croppedImageUrl;
  croppedImage.hidden = false;
});

// Cleanup when done
// result.cleanup();
// unsubscribe();

CSS Styling

#image-wheel-container {
  height: 300px;
  width: 200px;
  cursor: crosshair;
  transition: transform 500ms;
}

#image-wheel-container img {
  height: 100%;
  width: 100%;
  object-fit: cover;
}

.controls {
  display: flex;
  gap: 0.5rem;
  margin-top: 1rem;
  align-items: center;
}

.controls button {
  padding: 0.5rem 1rem;
  background: #f3f4f6;
  border-radius: 0.25rem;
  border: none;
  cursor: pointer;
  font-size: 0.875rem;
}

.controls button:hover {
  background: #e5e7eb;
}
Wheel zoom mode supports both mouse wheel scrolling and pinch-to-zoom gestures on touch devices. The library automatically handles these interactions.

Hover Zoom Mode

Hover zoom displays a magnified version of the image in a separate target element when the user hovers over the image.

HTML Structure

<div id="image-hover-container" class="relative flex h-[300px] w-[200px]">
  <img class="h-full w-full" alt="Product Image" src="/product.jpg" />
  <div id="zoom-hover-target" class="absolute left-[350px]"></div>
</div>

JavaScript Implementation

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

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

const result = createZoomImageHover(container, {
  zoomImageSource: "/product-large.jpg", // Optional: use higher resolution image
  customZoom: { width: 300, height: 500 },
  zoomTarget: zoomTarget,
  scale: 2,
  zoomLensClass: "custom-lens", // Optional: custom lens styling
});

// Cleanup when done
// result.cleanup();

CSS Styling

#image-hover-container {
  position: relative;
  display: flex;
  height: 300px;
  width: 200px;
}

#image-hover-container img {
  height: 100%;
  width: 100%;
  object-fit: cover;
}

#zoom-hover-target {
  position: absolute;
  left: 350px;
  border: 1px solid #e5e7eb;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

/* Custom lens styling */
.custom-lens {
  border: 2px solid #6366f1;
  background: rgba(99, 102, 241, 0.2);
}
You can use a higher resolution image for zoomImageSource to show more detail in the zoomed view, while keeping a lower resolution image for the main display.

Move Zoom Mode

Move zoom displays a magnified version of the image that follows the cursor as it moves across the image.

HTML Structure

<div id="image-move-container" class="relative h-[300px] w-[200px] cursor-crosshair overflow-hidden">
  <img class="h-full w-full" alt="Product Image" src="/product.jpg" />
</div>

JavaScript Implementation

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

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

const result = createZoomImageMove(container, {
  zoomImageSource: "/product-large.jpg", // Optional: use higher resolution image
  zoomFactor: 4,
  disabledContextMenu: false,
});

// Subscribe to state changes
const unsubscribe = result.subscribe(({ state }) => {
  console.log("Zoom image status:", state.zoomedImgStatus);
});

// Cleanup when done
// result.cleanup();
// unsubscribe();

CSS Styling

#image-move-container {
  position: relative;
  height: 300px;
  width: 200px;
  cursor: crosshair;
  overflow: hidden;
  border: 1px solid #e5e7eb;
}

#image-move-container img {
  height: 100%;
  width: 100%;
  object-fit: cover;
}

Click Zoom Mode

Click zoom activates the zoom when the user clicks on the image, then follows the cursor movement.

HTML Structure

<div id="image-click-container" class="relative h-[300px] w-[200px] cursor-crosshair overflow-hidden">
  <img class="h-full w-full" alt="Product Image" src="/product.jpg" />
</div>

JavaScript Implementation

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

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

const result = createZoomImageClick(container, {
  zoomImageSource: "/product-large.jpg", // Optional: use higher resolution image
  zoomFactor: 4,
  disableScrollLock: false,
});

// Subscribe to state changes
const unsubscribe = result.subscribe(({ state }) => {
  console.log("Zoom image status:", state.zoomedImgStatus);
});

// Cleanup when done
// result.cleanup();
// unsubscribe();

CSS Styling

#image-click-container {
  position: relative;
  height: 300px;
  width: 200px;
  cursor: crosshair;
  overflow: hidden;
  border: 1px solid #e5e7eb;
}

#image-click-container img {
  height: 100%;
  width: 100%;
  object-fit: cover;
}
Click to activate zoom, move the cursor to navigate the zoomed image, then click again to deactivate.

Complete Example with Tab Switching

Here’s a complete example that allows switching between all zoom modes:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Zoom Image Demo</title>
  <style>
    .tabs {
      display: flex;
      gap: 1rem;
      margin-bottom: 1rem;
    }
    .tab {
      padding: 0.5rem 1rem;
      cursor: pointer;
      border-radius: 0.375rem;
      background: white;
      border: 1px solid #e5e7eb;
    }
    .tab.active {
      background: #f3f4f6;
    }
    .zoom-container {
      height: 300px;
      width: 200px;
      cursor: crosshair;
    }
    .zoom-container img {
      height: 100%;
      width: 100%;
      object-fit: cover;
    }
  </style>
</head>
<body>
  <nav class="tabs">
    <button class="tab active" data-mode="wheel">Wheel</button>
    <button class="tab" data-mode="hover">Hover</button>
    <button class="tab" data-mode="move">Move</button>
    <button class="tab" data-mode="click">Click</button>
  </nav>
  
  <div id="parent"></div>
  
  <script type="module" src="./main.js"></script>
</body>
</html>
// main.js
import {
  createZoomImageWheel,
  createZoomImageHover,
  createZoomImageMove,
  createZoomImageClick,
} from "@zoom-image/core";

const parent = document.getElementById("parent");
let cleanupZoom = () => {};

function initWheel() {
  parent.innerHTML = `
    <div id="container" class="zoom-container">
      <img src="/product.jpg" alt="Product" />
    </div>
  `;
  const container = document.getElementById("container");
  const result = createZoomImageWheel(container);
  cleanupZoom = result.cleanup;
}

function initHover() {
  parent.innerHTML = `
    <div id="container" class="zoom-container" style="display: flex;">
      <img src="/product.jpg" alt="Product" />
      <div id="zoom-target" style="position: absolute; left: 350px;"></div>
    </div>
  `;
  const container = document.getElementById("container");
  const zoomTarget = document.getElementById("zoom-target");
  const result = createZoomImageHover(container, {
    zoomTarget,
    customZoom: { width: 300, height: 500 },
    scale: 2,
  });
  cleanupZoom = result.cleanup;
}

function initMove() {
  parent.innerHTML = `
    <div id="container" class="zoom-container" style="overflow: hidden;">
      <img src="/product.jpg" alt="Product" />
    </div>
  `;
  const container = document.getElementById("container");
  const result = createZoomImageMove(container);
  cleanupZoom = result.cleanup;
}

function initClick() {
  parent.innerHTML = `
    <div id="container" class="zoom-container" style="overflow: hidden;">
      <img src="/product.jpg" alt="Product" />
    </div>
  `;
  const container = document.getElementById("container");
  const result = createZoomImageClick(container);
  cleanupZoom = result.cleanup;
}

const modes = {
  wheel: initWheel,
  hover: initHover,
  move: initMove,
  click: initClick,
};

const tabs = document.querySelectorAll(".tab");
tabs.forEach((tab) => {
  tab.addEventListener("click", () => {
    cleanupZoom();
    tabs.forEach((t) => t.classList.remove("active"));
    tab.classList.add("active");
    const mode = tab.dataset.mode;
    modes[mode]();
  });
});

// Initialize with wheel mode
initWheel();

Best Practices

Call the cleanup() method when you’re done with a zoom instance to remove event listeners and prevent memory leaks:
const result = createZoomImageWheel(container);

// Later, when removing the component
result.cleanup();
When adding custom event listeners related to zoom functionality, use AbortController for easy cleanup:
const controller = new AbortController();
const { signal } = controller;

button.addEventListener("click", handleClick, { signal });

// Cleanup all listeners at once
controller.abort();
Always unsubscribe from state changes when you’re done:
const unsubscribe = result.subscribe(({ state }) => {
  console.log(state);
});

// Later
unsubscribe();

Next Steps

Styling Guide

Learn how to customize the appearance of zoom components

Configuration

Explore all available configuration options

Advanced Usage

Master advanced features and patterns

API Reference

Detailed API documentation for all zoom modes

Build docs developers (and LLMs) love