@get-set/gs-zoom
v0.0.13
Published
Get-Set Zoom
Maintainers
Readme
GSZoom
A dependency-free image lightbox & magnifier with drag, pinch-zoom, wheel-zoom, autoplay, fullscreen, a thumbnail filmstrip and keyboard navigation — available in two flavours from one codebase:
- Native / vanilla JS — a
window.GSZoom(images, params)factory (also exposed as a jQuery plugin and anHTMLElement.prototypemethod). - React — a
<GSZoom />component.
Both share the exact same engine (actions/, constants/, helpers/, types/), so behaviour is identical across the two.
Features
- Two modes:
lightbox(full-screen viewer over a collection of images) andmagnifier(in-place zoom lens that follows the cursor). - Backdrop / overlay catalog:
dim,blur,glass,gradient,vignette,tint,none— tunable colour, opacity and blur radius. - Open / close animation catalog:
flip(flies from the clicked thumbnail via the FLIP technique),fade,zoom,slide,flipIn,none— tunable duration and easing. - Themeable chrome (
dark/light) plus a customaccentcolour for active states, progress, spinner and the active thumbnail. - Opt-in frosted
glassControlsfor the prev / next / close / zoom buttons. - Drag to pan, pinch-to-zoom (touch), mouse-wheel zoom, and double-tap zoom, with a tunable
maxZoomceiling. - Prev / next arrows and ← → keyboard navigation, with wrap-around.
- Thumbnail filmstrip (
showAdditionals) that can be kept open by default (showThumbnails). - Autoplay with a loading-progress ring and a tunable interval.
- Fullscreen on open (can be disabled), with the lightbox restored on exit.
- Per-image
data-gs*attributes for title / subtitle / description and an alternate high-res magnifier source. - Query-string injection for main and thumbnail image URLs (CDN sizing, cache-busting, etc.).
- Responsive
breakpointsoverriding any param (and the magnifier config) per viewport width. - Lifecycle hooks:
beforeInit,afterInit,beforeLightBoxOpen,afterLightBoxOpen,beforeLightBoxClose,afterLightBoxClose,afterChange.
Compatibility
| Target | Requirement |
|---|---|
| React (the <GSZoom> component) | React 16.8+ (Hooks are required), and 17 / 18 / 19. React is an optional peer dependency — you only need it for the component. |
| Native / vanilla (window.GSZoom) | No framework. Any modern evergreen browser. Optional jQuery ($(...).GSZoom(...)) and HTMLElement.prototype.GSZoom(...) adapters are registered automatically if present. |
| TypeScript | First-class — type declarations (.d.ts) ship in the package. |
| SSR / Next.js | Safe to import server-side (no DOM access at module load). The viewer itself is browser-only, so render it inside a Client Component ('use client'). |
The peer range is
^16.8.0 || ^17.0 || ^18.0 || ^19.0, withreact/react-dommarked optional so the native build has zero framework dependencies.
Installation
npm i @get-set/gs-zoomProject layout
GSZoom.ts # native entry (webpack -> dist-js/bundle.js, window global)
components/GSZoom.tsx # React component (tsc -> dist/, npm entry)
actions/ # shared engine (open/close/change/draw/magnifier/autoplay/drag…)
constants/ # shared constants (defaults, type/backdrop/animation/theme catalogs, icons)
helpers/uihelpers.ts # shared helpers
types/ # Params / Ref / Window augmentation
components/styles/ # SCSS + compiled CSS + CSS-as-TS (runtime injection for React)
styles/ # SCSS + compiled CSS (for <link> use by the native build)Build
npm install
npm run build # builds both targets
npm run build:js # native bundle -> dist-js/bundle.js
npm run build:react # React + types -> dist/components/GSZoom.jsTests
Unit tests use Vitest + jsdom:
npm test # run once
npm run test:watchUsage — Native JS
The factory takes a single image, a NodeList, or an array of <img> elements, plus the params object. Include the stylesheet and the bundle, then call new GSZoom(...):
<link rel="stylesheet" href="styles/GSZoom.css" />
<!-- Lightbox on a collection -->
<div class="gallery">
<img src="photo1.jpg" data-gstitle="Photo 1" data-gssubtitle="Subtitle" />
<img src="photo2.jpg" data-gsdescription="A longer caption." />
<img src="photo3.jpg" data-gszoomsrc="photo3-hi-res.jpg" />
</div>
<script src="dist-js/bundle.js"></script>
<script>
new GSZoom(document.querySelectorAll('.gallery img'), {
type: 'lightbox',
arrows: true,
navigateWithKeys: true,
maxZoom: 5,
backdrop: 'glass',
animation: 'flip',
theme: 'dark',
accent: '#34d1ff',
autoplaySpeed: 4000,
responsive: [
{ windowSize: 768, params: { arrows: false } },
],
afterLightBoxOpen: () => console.log('opened'),
});
</script>Equivalent jQuery / element forms (registered by the bundle):
// jQuery
$('.gallery img').GSZoom({ type: 'lightbox' });
// HTMLElement.prototype
document.querySelector('img').GSZoom({ type: 'magnifier' });Note: the jQuery
$(sel).GSZoom(...)andel.GSZoom(...)forms initialise each matched element as its own viewer (jQuery's per-element convention) — ideal for standalone images or magnifiers. For a single navigable gallery (prev/next across the whole set), pass the images to thewindow.GSZoom(images, params)factory, or render the React<GSZoom>with the images as children.
Instance registry & methods
Every instance is stored in a global registry keyed by its reference. Look one up with window.GSZoomConfigue.instance(key) to get its raw Ref (state object). The registry also exposes references (the list of { key, ref }) and openedZoom (the reference of the currently open lightbox, or undefined).
new GSZoom(document.querySelectorAll('.gallery img'), { reference: 'main' });
const ref = window.GSZoomConfigue.instance('main'); // the Ref for this instance
window.GSZoomConfigue.openedZoom; // which lightbox is open (if any)The imperative actions are importable functions in the native build that operate on a Ref: initOpen(ref, index), initClose() and initChange(ref, index) (advance/rewind is initChange with the next/previous index). The React component ref wraps these as open / close / next / prev (below) — see Methods.
Usage — React
import GSZoom from '@get-set/gs-zoom';
// Lightbox — collection mode (wrap multiple images)
<GSZoom type="lightbox" arrows navigateWithKeys maxZoom={5} backdrop="glass" animation="flip">
<img src="photo1.jpg" data-gstitle="Photo one" data-gssubtitle="Subtitle" />
<img src="photo2.jpg" />
</GSZoom>
// Lightbox — single image mode (use the `src` prop, no children)
<GSZoom type="lightbox" src="photo1.jpg" data-gstitle="Photo one" />
// Magnifier
<GSZoom
type="magnifier"
src="product.jpg"
data-gszoomsrc="product-hi-res.jpg"
magnifier={{ zoom: 4, form: 'circle', size: 180 }}
/>In collection mode each <img> you pass as a child is registered automatically (the wrapper gets a .gs-zoom-collection class). In single-image mode pass the image via the src prop instead of children. The CSS is injected at runtime — no stylesheet import required. The React-only prop gsx lets you scope inline styles to a single instance.
Next.js (App Router)
The viewer is browser-only, so use it from a Client Component:
'use client';
import GSZoom from '@get-set/gs-zoom';
export default function Gallery() {
return (
<GSZoom type="lightbox" arrows backdrop="vignette">
<img src="/1.jpg" />
<img src="/2.jpg" />
</GSZoom>
);
}Options
Every option is optional; the table lists the default applied when omitted.
| Option | Type | Default | Description |
|---|---|---|---|
| reference | string | random GUID | Unique key (used by the registry / instance() lookup). |
| type | 'lightbox' \| 'magnifier' | 'lightbox' | Plugin mode — full-screen viewer or in-place zoom lens. |
| arrows | boolean | true | Show prev / next arrow buttons in the lightbox. |
| navigateWithKeys | boolean | true | Enable ← → keyboard navigation. |
| showAdditionals | boolean | true | Build the bottom thumbnail strip in the lightbox. |
| showThumbnails | boolean | false | Keep the thumbnail filmstrip open by default (instead of toggled). |
| zoomOnWheel | boolean | true | Zoom the image with the mouse wheel. |
| maxZoom | number | 5 | Maximum zoom multiplier. |
| disableFullScreen | boolean | false | Don't request the Fullscreen API when the lightbox opens. |
| imgLoading | string | '' | HTML string shown in place of the image while it loads. |
| autoplaySpeed | number | 5000 | Autoplay interval (ms) between images. |
| mainImageQueryParameters | string | '' | Query string appended to the main (full-size) image src. |
| additionalImageQueryParameters | string | '' | Query string appended to each thumbnail src. |
| backdrop | 'dim' \| 'blur' \| 'glass' \| 'gradient' \| 'vignette' \| 'tint' \| 'none' | 'dim' | Overlay layer rendered behind the lightbox. See Backdrops. |
| backdropColor | string | '' | Base colour (any CSS colour) for dim / glass / gradient / vignette / tint. |
| backdropOpacity | number | 0.92 | Backdrop opacity, 0–1. |
| backdropBlur | number | 50 | Blur radius (px) for the blur / glass backdrops. |
| animation | 'flip' \| 'fade' \| 'zoom' \| 'slide' \| 'flipIn' \| 'none' | 'flip' | Open / close transition. See Animations. |
| animationDuration | number | 420 | Open / close transition duration (ms). |
| animationEasing | string | 'cubic-bezier(0.22, 1, 0.36, 1)' | Any CSS easing for the open / close transition. |
| theme | 'dark' \| 'light' | 'dark' | Lightbox chrome theme. See Themes. |
| accent | string | '' | Accent colour (any CSS colour) for the counter, autoplay progress, spinner and active thumbnail. |
| glassControls | boolean | true | Render the prev / next / close / zoom controls as frosted-glass buttons. |
| showCounter | boolean | true | Show the n / total counter in the header. |
| magnifier | Partial<MagnifierParams> | see MagnifierParams | Magnifier-mode settings. |
| responsive | ResponsiveOption[] | [] | Per-breakpoint overrides (sorted automatically). See ResponsiveOption. |
| gsx | NestedCSS | – | React only — scoped inline styles for this instance (see gsx). |
React component props
In addition to every option above, the React <GSZoom> component accepts:
| Prop | Type | Description |
|---|---|---|
| src | string | Single-image mode — renders one <img> from this URL instead of reading children. |
| children | React.ReactNode | Collection mode — every <img> inside is registered as a gallery item. |
Any other DOM attributes you pass (e.g. className, data-gstitle) are forwarded to the rendered <img> (single mode) — plugin-only props are stripped so they never leak onto the DOM node.
MagnifierParams
Passed via the magnifier option.
| Prop | Type | Default | Description |
|---|---|---|---|
| zoom | number | 3 | Zoom multiplier inside the lens. |
| form | 'circle' \| 'square' | 'circle' | Shape of the magnifier lens. |
| size | number | 150 | Lens diameter / side length (px). |
ResponsiveOption
Each entry in the responsive array overrides params (and optionally the magnifier config) at or below windowSize.
| Prop | Type | Description |
|---|---|---|
| windowSize | number | Breakpoint width (px). |
| params | Partial<Params> | Params to apply at this breakpoint. |
| magnifier | Partial<MagnifierParams> | Magnifier overrides at this breakpoint (optional). |
Per-image data attributes
Set these on the <img> elements (works in both vanilla and React).
| Attribute | Used by | Description |
|---|---|---|
| data-gstitle | lightbox | Title text shown below the image. |
| data-gssubtitle | lightbox | Subtitle text. |
| data-gsdescription | lightbox | Description / caption text. |
| data-gszoomsrc | magnifier | Alternate high-res source used inside the lens (falls back to src). |
Callbacks
All are optional () => void hooks.
| Callback | Fires |
|---|---|
| beforeInit | before the plugin initialises |
| afterInit | after the plugin initialises |
| beforeLightBoxOpen | before the lightbox opens |
| afterLightBoxOpen | after the lightbox opens |
| beforeLightBoxClose | before the lightbox closes |
| afterLightBoxClose | after the lightbox closes |
| afterChange | after the active image changes |
Backdrop catalog
The backdrop option selects the overlay layer rendered behind the lightbox (each maps to a gs-zoom-backdrop-<name> class on the container).
| Value | Description |
|---|---|
| dim | Solid dark dim layer (classic, default). |
| blur | Frosted blur of the page behind the lightbox (uses backdropBlur). |
| glass | Blur + translucent tint + a hairline border (glassmorphism). |
| gradient | Radial / linear gradient wash. |
| vignette | Darkened edges fading to a clearer centre. |
| tint | Flat solid colour fill (uses backdropColor). |
| none | No backdrop at all (transparent). |
Tune any of them with backdropColor, backdropOpacity and backdropBlur.
Animation catalog
The animation option selects the open / close transition. Tune it with animationDuration and animationEasing.
| Value | Description |
|---|---|
| flip | FLIP zoom-from-thumbnail — the image flies from the clicked thumbnail's rect to full screen (default). |
| fade | Simple cross-fade. |
| zoom | Scale up from the centre. |
| slide | Slide up from the bottom. |
| flipIn | 3D flip-in. |
| none | No animation. |
Theme catalog
The theme option sets the chrome palette.
| Value | Description |
|---|---|
| dark | Dark chrome (default). |
| light | Light chrome. |
Layer a custom accent colour on top of either theme.
The gsx prop (React only)
gsx accepts a nested CSS map that is compiled and injected scoped to this instance only (matched on the instance's data-key), so you can theme one viewer without a global stylesheet:
<GSZoom
type="lightbox"
gsx={{ '.gs-zoom-counter': { fontWeight: 700, letterSpacing: '0.04em' } }}
>
<img src="photo1.jpg" />
<img src="photo2.jpg" />
</GSZoom>Methods (imperative API)
| Method | Description |
|---|---|
| open(index?) | Open the lightbox at a 0-based index (defaults to the current index). |
| close() | Close the currently open lightbox. |
| next() / prev() | Advance / rewind by one image (wraps around). |
| getInstance() | React only — returns the raw plugin instance (registry Ref) for this component. |
Access — Native — the registry stores each instance's Ref (state object) under its reference:
new GSZoom(document.querySelectorAll('.gallery img'), { reference: 'main' });
const z = window.GSZoomConfigue.instance('main'); // raw Ref { list, currentParams, currentIndex, … }Access — React (via a ref to the component):
import GSZoom, { GSZoomHandle } from '@get-set/gs-zoom';
import { useRef } from 'react';
function Gallery() {
const ref = useRef<GSZoomHandle>(null);
return (
<>
<button onClick={() => ref.current?.open(0)}>Open first</button>
<GSZoom ref={ref} type="lightbox">
<img src="photo1.jpg" />
<img src="photo2.jpg" />
</GSZoom>
</>
);
}
next()/prev()only act while this instance's lightbox is the one currently open (window.GSZoomConfigue.openedZoom); they are no-ops otherwise.
License
ISC.
