
SmartPhoto is a vanilla JavaScript image lightbox that opens full-size images in a responsive, touch-friendly viewer.
It supports pinch-to-zoom, drag, swipe, thumbnail navigation, captions, and grouped galleries.
Features:
- Pinch zoom, drag, and swipe gestures for touch screens.
- Full-size image viewing from existing thumbnail links.
- JavaScript array data source for dynamic galleries.
- Independent image groups within the same page.
- URL hash links for individual photos.
- Optional navigation arrows and thumbnail strip.
- Keyboard and screen-reader support.
- Programmatic navigation, zoom, opening, and closing controls.
- CSS custom properties for animation timing and viewer colors.
How To Use It
Installation
Load the SmartPhoto stylesheet and JavaScript build before initializing the gallery:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/smartphoto/css/smartphoto.min.css" /> <script src="https://cdn.jsdelivr.net/npm/smartphoto/lib/smartphoto.min.js"></script>
Or install the package with NPM:
npm install smartphoto
Then import it into your project:
import SmartPhoto from "smartphoto"; import "smartphoto/css/smartphoto.min.css";
Basic Usage
Each gallery item uses an anchor for the full-size image and an <img> for the thumbnail. SmartPhoto scans elements that match the selector passed to its constructor.
hrefsupplies the full-size image URL.data-captionadds text beneath the active image.data-ididentifies the photo for programmatic opening and URL hash links.data-groupplaces related photos in the same gallery.alton the thumbnail supplies image alternative text.data-srcsupplies a lazy-loaded thumbnail URL when you keep the defaultlazyAttributesetting.
<a
href="/images/coast-large.jpg"
class="js-smartphoto"
data-caption="Rocky coastline"
data-id="coast"
data-group="travel"
>
<img
src="/images/coast-thumb.jpg"
alt="Rocky coastline"
width="320"
/>
</a>
<a
href="/images/harbor-large.jpg"
class="js-smartphoto"
data-caption="Harbor at sunset"
data-id="harbor"
data-group="travel"
>
<img
src="/images/harbor-thumb.jpg"
alt="Harbor at sunset"
width="320"
/>
</a>
<script>
document.addEventListener("DOMContentLoaded", function () {
const gallery = new SmartPhoto(".js-smartphoto");
});
</script>
Create a Gallery from JavaScript Data
The constructor also accepts an array of slide objects. This mode is ideal for photo galleries populated from an API, JSON response, CMS payload, or application state.
Provide width and height when those values already exist in your data. SmartPhoto skips its image-size measurement preload for slides that include both dimensions.
Slide objects support these fields:
src(required): Full-size image URL.thumb: Thumbnail URL. SmartPhoto falls back tosrc.caption: Caption text.alt: Alternative text. SmartPhoto falls back to the caption, thensrc.id: String or number used byshow()and URL hashes. SmartPhoto falls back to the slide index.group: Gallery group name. The default group isnogroup.widthandheight: Natural image dimensions in pixels.
const gallery = new SmartPhoto([
{
src: "/images/mountain-large.jpg",
thumb: "/images/mountain-thumb.jpg",
caption: "Mountain ridge",
alt: "Snow-covered mountain ridge",
id: "mountain",
group: "landscapes",
width: 1600,
height: 1067
},
{
src: "/images/lake-large.jpg",
thumb: "/images/lake-thumb.jpg",
caption: "Alpine lake",
alt: "Blue alpine lake",
id: "lake",
group: "landscapes",
width: 1600,
height: 1067
}
]);
gallery.show(0, { group: "landscapes" });
// An ID works as well.
gallery.show("lake", { group: "landscapes" });
Open Photos Programmatically
show() accepts either an image index or ID. The optional group selects a specific gallery, while trigger identifies the element used for the opening transition and focus return.
const gallery = new SmartPhoto(".js-smartphoto");
gallery.show(2);
gallery.show("harbor");
gallery.show("harbor", {
group: "travel",
trigger: document.querySelector("#open-travel-gallery")
});
Configuration Options
You can pass an options object as the second constructor argument. Full options:
arrows(boolean, defaulttrue): Shows previous and next arrow controls.nav(boolean, defaulttrue): Shows the thumbnail navigation strip.showAnimation(boolean, defaulttrue): Controls the opening and closing transition.verticalGravity(boolean, defaultfalse): Adds vertical device-tilt movement when orientation control is active.useOrientationApi(boolean, defaultfalse): Uses device orientation data to move a zoomed image.useHistoryApi(boolean, defaulttrue): Updates the URL hash with the active group and photo.swipeTopToClose(boolean, defaultfalse): Closes the viewer after an upward swipe.swipeBottomToClose(boolean, defaulttrue): Closes the viewer after a downward swipe.swipeOffset(number, default100): Sets the minimum swipe distance in pixels.swipeVelocity(number, default0.5): Sets the minimum swipe speed in pixels per millisecond for a fast flick.headerHeight(number, default60): Reserves vertical space for the header during image fitting.footerHeight(number, default60): Reserves vertical space for the footer during image fitting.resizeStyle("fit" | "fill", default"fit"): Chooses how images scale inside the available viewer area.animationSpeed(number, default300): Sets transition duration in milliseconds.forceInterval(number, default10): Sets the interval used for force calculations during image movement.registance(number, default0.5): Controls friction for inertial movement on a zoomed image. Keep theregistancespelling when setting this option.loadOffset(number, default2): Sets how many nearby slides SmartPhoto preloads around the active photo.lazyAttribute(string, default"data-src"): Selects the thumbnail attribute used for lazy-loaded images in HTML mode.classNames(object): Overrides individual generated SmartPhoto class names.message(object): Overrides screen-reader text for next, previous, close, and carousel labels.
const gallery = new SmartPhoto(".js-smartphoto", {
arrows: true,
nav: false,
resizeStyle: "fit",
animationSpeed: 250,
loadOffset: 1
});
The message object accepts four text values:
gotoNextImage: Label for next-image navigation.gotoPrevImage: Label for previous-image navigation.closeDialog: Label for the close control.carouselLabel: Accessible name for the image collection.
Disable Arrows or Thumbnail Navigation
A minimal viewer can remove either navigation interface during initialization.
const gallery = new SmartPhoto(".js-smartphoto", {
arrows: false,
nav: false
});
Fit or Fill Images
resizeStyle accepts fit or fill. Use fit when the entire image should stay inside the available viewer area.
const gallery = new SmartPhoto(".js-smartphoto", {
resizeStyle: "fit"
});
Styling and CSS Custom Properties
SmartPhoto exposes four CSS custom properties for its main visual settings. The JavaScript animationSpeed option controls per-instance animation timing.
--smartphoto-animation-speed: Transition duration. Default300ms.--smartphoto-animation-function: Transition easing. Defaultease-out.--smartphoto-backdrop-color: Viewer backdrop color. Defaultrgba(0, 0, 0, 1).--smartphoto-header-color: Header background color. Defaultrgba(0, 0, 0, 0.2).
Set the variables on .smartphoto for viewer-specific styling or :root for site-wide defaults.
.smartphoto {
--smartphoto-animation-speed: 450ms;
--smartphoto-animation-function: ease-in-out;
--smartphoto-backdrop-color: rgba(10, 12, 18, 0.96);
--smartphoto-header-color: rgba(10, 12, 18, 0.55);
}
API Methods
const gallery = new SmartPhoto(".js-smartphoto");
// Subscribe to a SmartPhoto event.
gallery.on("change", function (event) {
console.log(event);
});
// Remove the viewer and its registered event listeners.
gallery.destroy();
// Invoke the disposable cleanup hook directly.
gallery[Symbol.dispose]();
// Move to a slide index in the active group.
gallery.gotoSlide(2);
// Close the viewer with an optional animation direction.
gallery.hidePhoto("top");
gallery.hidePhoto("bottom");
// Zoom the active image.
gallery.zoomPhoto();
// Return the active image to its normal scale.
gallery.zoomOutPhoto();
// Register a new thumbnail element in HTML mode.
gallery.addNewItem(document.querySelector("#new-photo"));
// Open by index or ID.
gallery.show(1);
gallery.show("harbor");
// Open a photo from a specific group.
gallery.show("harbor", {
group: "travel"
});
// Close the viewer.
gallery.hide();
// Navigate through the active group.
gallery.next();
gallery.prev();
// Add either a slide object or HTML element.
gallery.addItem({
src: "/images/new-large.jpg",
thumb: "/images/new-thumb.jpg",
caption: "New photo",
id: "new-photo",
group: "travel"
});
// Read the active slide index.
console.log(gallery.currentIndex);
Events
// Fires when the viewer opens.
gallery.on("open", function (event) {
console.log("open");
});
// Fires after the viewer closes.
gallery.on("close", function (event) {
console.log("close");
});
// Fires after the active photo changes.
gallery.on("change", function (event) {
console.log("change");
});
// Fires when all images in a group have loaded.
gallery.on("loadall", function (event) {
console.log("loadall");
});
// Fires when a swipe begins.
gallery.on("swipestart", function (event) {
console.log("swipestart");
});
// Fires when a swipe ends.
gallery.on("swipeend", function (event) {
console.log("swipeend");
});
// Fires when a pinch gesture begins.
gallery.on("gesturestart", function (event) {
console.log("gesturestart");
});
// Fires when a pinch gesture ends.
gallery.on("gestureend", function (event) {
console.log("gestureend");
});
// Fires after SmartPhoto zooms into the active image.
gallery.on("zoomin", function (event) {
console.log("zoomin");
});
// Fires after SmartPhoto returns the image to normal scale.
gallery.on("zoomout", function (event) {
console.log("zoomout");
});
Alternatives:
- Modern JavaScript Lightbox with Zoom & Gallery – Zoomora
- Vanilla JS Gallery Lightbox with Touch Support – Web-Thread Lightbox
- Accessible Gallery Lightbox with Vanilla JS – Robroy
- Lightweight Gallery Lightbox with Zoom, Pan, and Keyboard Navigation – ImageViewer.js
FAQs:
Q: How do I create multiple SmartPhoto galleries on one page?
A: Give related HTML thumbnails the same data-group value. Slide-array mode uses the group field, and show() accepts a group option when you need to open a specific collection.
Q: Why doesn’t tilting the device move a zoomed image?
A: SmartPhoto sets useOrientationApi to false by default. Set it to true during initialization when your interface needs device-orientation movement.
Q: How should I remove SmartPhoto from a dynamic application?
A: Call destroy() before permanently discarding the instance. The method removes the viewer and its registered event listeners.
Changelog:
v2.1.16 (09/11/2026)
- update








Is there any option to loop. when image end starts from first. arrow will always show.
How to disable the (bottom par)t images thumbnails ?
The data-group attribute needs to be on the anchor element instead of the image within in order to work.