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 : 300 px ;
width : 200 px ;
cursor : crosshair ;
transition : transform 500 ms ;
}
#image-wheel-container img {
height : 100 % ;
width : 100 % ;
object-fit : cover ;
}
.controls {
display : flex ;
gap : 0.5 rem ;
margin-top : 1 rem ;
align-items : center ;
}
.controls button {
padding : 0.5 rem 1 rem ;
background : #f3f4f6 ;
border-radius : 0.25 rem ;
border : none ;
cursor : pointer ;
font-size : 0.875 rem ;
}
.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 : 300 px ;
width : 200 px ;
}
#image-hover-container img {
height : 100 % ;
width : 100 % ;
object-fit : cover ;
}
#zoom-hover-target {
position : absolute ;
left : 350 px ;
border : 1 px solid #e5e7eb ;
box-shadow : 0 4 px 6 px rgba ( 0 , 0 , 0 , 0.1 );
}
/* Custom lens styling */
.custom-lens {
border : 2 px 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 : 300 px ;
width : 200 px ;
cursor : crosshair ;
overflow : hidden ;
border : 1 px 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 : 300 px ;
width : 200 px ;
cursor : crosshair ;
overflow : hidden ;
border : 1 px 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 : 1 rem ;
margin-bottom : 1 rem ;
}
.tab {
padding : 0.5 rem 1 rem ;
cursor : pointer ;
border-radius : 0.375 rem ;
background : white ;
border : 1 px solid #e5e7eb ;
}
.tab.active {
background : #f3f4f6 ;
}
.zoom-container {
height : 300 px ;
width : 200 px ;
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
Always cleanup when removing zoom instances
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 ();
Use AbortController for additional event listeners
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 ();
Subscribe and unsubscribe to state changes
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