zoom.js, a dependency-free image zoom plugin
Image zoom plugin with no dependencies (fork of fat/zoom.js)
At a glance
- What is it?
- zoom.js is a small image zoom plugin in the style of the one medium.com used to ship, and version 4 is a TypeScript rewrite with no jQuery, no Bootstrap and no dependencies at all. The API is two functions and a four field configuration object, and the project is explicit that it makes no backwards compatibility promises even within a major version.
- Who is it for?
- zoom.js fits a project that needs a lightbox today and does not want a jQuery dependency or a framework specific component to carry it, since the whole thing is a TypeScript module with two exported functions and a stylesheet you link yourself.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 26 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A lightbox without jQuery
zoom.js is an image zooming plugin, as seen on older versions of medium.com, and the project describes itself as a port of fat/zoom.js that has no jQuery or Bootstrap dependencies. That is the whole appeal. The original library was a jQuery plugin, which meant adopting it meant adopting jQuery, and most of the sites that wanted the effect did not have it. Version 4 is written in TypeScript, has a new API, includes typings, and has no dependencies, and the package is published to npm as @nishanths/zoom.js. The behaviour is the familiar one. Clicking an image zooms it, and the zoom is dismissed by clicking the image again, clicking the overlay around it, scrolling away, or pressing the escape key. The audience is a developer with a static site or a server rendered application who wants a decent lightbox without taking on a framework.
The v4 branch, the frozen master, and a compatibility warning
The repository has two branches and the README is precise about what each one is. The v4 branch is the default branch and holds the code for version 4, which is the current major version. The master branch is frozen and no longer maintained, and the final version on it is 3.1.0, so a project still importing the old package from master is on a dead line. Then there is the sentence that should stop a careful reader. There are no API backwards compatibility guarantees even within the same major version, so if necessary you should pin an exact version and upgrade manually. That is unusual and worth taking at face value. A minor release of this package can therefore change behaviour, and the only defence the project offers is for you to pin the version and do the upgrade on your own schedule.
Installing it, and the module field that bites
Installation is an npm install, the stylesheet is linked by hand, and the function is imported.
npm i @nishanths/zoom.js<link href="zoom.css" rel="stylesheet">import { zoom } from "@nishanths/zoom.js"The stylesheet path in the link tag is the one in the source tree, so a build step or a copy is needed to get it into your application, and the file is also copied into dist by the build. The trap is described in the README. The package.json for the package specifies the module property but not the main property, and you may need a module-aware tool to correctly include the package in your bundle, with a linked Stack Overflow answer offered as the starting point. The package manifest confirms it, with main set to an empty string and module pointing at dist/index.js, plus types at dist/index.d.ts. The JavaScript files in the dist directory are ES modules, so a CommonJS-only toolchain is the case that needs attention.
Two functions and a configuration object
The exported API is small enough to read in one screen. The zoom function takes an HTMLImageElement and an optional configuration, and it returns nothing. The documentation states the important edge case directly, that the image will not be zoomed if its naturalWidth or naturalHeight property is 0, which is usually because the values are unavailable, so an image that has not been laid out or has no intrinsic size simply does nothing. The second export is dismissZoom, which dismisses the presently active zoom and is a no-op if there is no zoom active at the time of the call, which makes it safe to call defensively. The configuration type has four fields. padding sets the horizontal and vertical space around the zoomed image, and paddingNarrow is the same thing for a narrow viewport, where the larger padding might produce poor results, with the note that paddingNarrow should be less than or equal to padding but that this is not validated. dismissScrollDelta is the vertical scrolling threshold that dismisses the zoom, and dismissTouchDelta is the equivalent threshold for touch movement.
What the plugin does to your page while it is open
The notes section documents the side effects, and an adopter should read them before shipping. All CSS class names the package uses are prefixed with zoom-, and adding the class zoom-cursor to a zoomable image switches it to a zoom-in cursor instead of the default one. The overlay element is appended to the end of document.body when an image is zoomed. While a zoom is active, the program listens for click events on document.body with useCapture set to true, and the handler calls e.stopPropagation(), and the documentation says plainly that this may interfere with other click event handlers on the page. The listener is removed when the zoom is dismissed. The image's transform style is replaced with the value needed for the zoom while it is active and the old transform is restored afterwards. In other words, the plugin takes over the body click path for the duration of the zoom, which is the price of a library that appends its own overlay rather than asking you for a container.
Building it locally with two make targets
The build is a Makefile with a small set of targets, and the README documents the two you need.
% make deps
% make buildThe deps target runs npm ci, and the build target creates the dist directory, runs the TypeScript compiler through npx tsc, and copies src/zoom.css into dist. A clean target removes dist and the incremental build info, and a release target cleans, builds, bumps the version with npm version minor, pushes the tags and publishes. The repository also carries a .node-version file, a tsconfig.json, a demo/ directory with its own images, an index.html at the root, and a package-lock.json. The package itself is published with files set to dist and LICENSE.original, and the only development dependency is TypeScript at 5.0.4 or later, which is a fair reflection of how little the library needs.
Two licences in one repository, and a browser support note
The licence section is unusually careful and worth reading in full. The software is based on the original fat/zoom.js project, and the copyright notices and the licence notices from that original project are present in the LICENSE.original file at the root. The new source code and the modifications in this repository are licensed under MIT, in the LICENSE file. In practice that means the upstream notice travels with the package, since LICENSE.original is in the published files list, and a distributor has both sets of terms to satisfy. On compatibility, the README says that popular web browser versions released after 2016 should be supported and asks you to read the source code for the exact details, which is an honest way of saying that there is no support matrix. There is no GitHub release stream for this project, so the version you consume is the version on npm, and the branches described earlier are what tell you whether it is current.
Editorial conclusion
zoom.js fits a project that needs a lightbox today and does not want a jQuery dependency or a framework specific component to carry it, since the whole thing is a TypeScript module with two exported functions and a stylesheet you link yourself. It does not fit a team that cannot accept a package without backwards compatibility guarantees, because the project states plainly that there are none even inside a major version, and it does not fit a page whose images are sized only after layout, since the plugin refuses to zoom an image whose naturalWidth or naturalHeight is zero. Verify three things first: that your bundler is module aware, since the package manifest declares a module field and an empty main field, which is the most common reason it fails to resolve, which version you pin, because the guidance is to pin an exact version and upgrade manually, and whether the capture phase click handler on document.body will collide with the click handlers your page already has.
Frequently asked questions
What is zoom.js?
It is an image zooming plugin in the style of the one medium.com used, ported from fat/zoom.js and rewritten in TypeScript for version 4 with no jQuery, no Bootstrap and no dependencies at all. It is published to npm as @nishanths/zoom.js and includes typings.
How do I use the zoom.js package?
Install it with npm i @nishanths/zoom.js, link the stylesheet with a link tag pointing at zoom.css, and import the function with import { zoom } from "@nishanths/zoom.js". The built files in dist are ES modules, and the package declares a module field but an empty main field, so a module aware bundler is needed.
What does the zoom.js Config type contain?
Four fields: padding for the space around the zoomed image, paddingNarrow used when the viewport is too narrow, dismissScrollDelta as the vertical scrolling threshold that dismisses the zoom, and dismissTouchDelta as the equivalent touch movement threshold. A partial configuration can be built by spreading defaultConfig.
Does zoom.js guarantee backwards compatibility?
No. The README states there are no API backwards compatibility guarantees even within the same major version, so it recommends pinning an exact version and upgrading manually. The master branch is frozen at version 3.1.0 and no longer maintained, while v4 is the default branch.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nishanths-zoom-js)Was this useful?