Jekyll One

Fulltext Search

A lightbox is a helper which displays enlarged, almost screen-filling versions of images while dimming the remainder of the page. The technique, introduced by Lightbox Version 2, gained widespread popularity thanks to its simple style. The term lightbox has been employed since then for Javascript libraries to support such functionality.

PhotoSwipe Lightbox is a JavaScript library to create lightboxes placed on images in webpages. When you click such extended images, they open in a new, overlaying layer, darkening the background and focusing the image. Well-suited for desktops, browsers, and apps on mobile devices with touchscreens. The library provides a smooth and engaging way to showcase images on the web, especially on mobile devices.

Here’s a breakdown of its key functionalities:

Displays images in a lightbox

When you click on an image, it overlays the current page with a zoomed-in view of the image. Easily zoom in and out of images and pan around them. View images in full-screen for an immersive experience

Supports galleries

You can link multiple images together, allowing users to easily browse through a collection.

Touch-friendly

Designed with intuitive touch gestures for navigation (swiping, pinching, etc.). Supports touch gestures like swiping, pinching, and dragging.

Responsive

Adapts to different screen sizes and orientations.

Customizable

Offers various options for customization, such as themes, transitions, and social sharing features.

Keyboard Navigation

Navigate through images using keyboard shortcuts.

Social Sharing

Easily share images on social media platforms.

PhotoSwipe comes with a rich API. The programming interface allows developers to create individual user interfaces (UI) for pagination, navigation and more. Find a large number of complex examples on the pages of the UI Initiative project.

The rich API makes PhotoSwipe an excellent choice for web developers, not least for template systems like J1 Template. In the current version of the J1 template (version 2025.x), PhotoSwipe is the default slider and replaces the previously used carousels Owl Carousel, Slickslider, and Masterslider by their implementations based on PhotoSwipe.

The PhotoSwipe API documentation pages are based on PhotoSwipe version v5.4.4 for the current J1 template version 2025.x. The idea of providing this documentation is not to simply copy the original pages as duplicates. For better readability and usability, all pages are restructured and enhanced by code examples or improved description texts.

The documentation pages for the J1 Template project will be used for the AI-based chat client (planned for 4th quarter of 2025). The agent will be trained by all available documentation pages of the Template system to give users an easy-to-use source for using the J1 Template to create websites for their needs.

All previously used slider resources are available in version 2025.x for backward compatibility but are no longer used for the example pages of the built-in web.

Usage

See PhotoSwipe Documentation and PhotoSwipe Lightbox Methods for more examples.

Name Description

gallery

The gallery element. May be a DOM element, a NodeList, or a CSS selector string. The gallery is the container that holds all the slides – for example a Swiper slider container.

children

The clickable elements inside a gallery. May be a DOM element, a NodeList, or a CSS selector string. Typically this is 'a' so that each slide’s link becomes the trigger that opens the lightbox. If the option is not defined or set to false, the gallery’s root node is used as the only child.

Initialization

A complete PhotoSwipe Lightbox setup needs the lightbox JavaScript file (photoswipe-lightbox.min.js) and the PhotoSwipe core file (photoswipe.min.js), plus the main stylesheet (photoswipe.css). If you also want captions, add the Dynamic Caption plugin (photoswipe-caption-plugin.min.js) and its stylesheet (photoswipe-caption-plugin.css).

The lightbox – with the optional caption plugin attached – is then initialised like so:

<!-- Lightbox initialisation (JavaScript) -->
<script>
  const myLightbox = new PhotoSwipeLightbox({
    gallery:    '#my_gallery',
    children:   'a',
    pswpModule: PhotoSwipe,
    // ... other options
  });
  // Attach the caption plugin BEFORE myLightbox.init() is called.
  const captionPlugin = new PhotoSwipeDynamicCaption(myLightbox, {
    type: 'auto'
  });
  // Initialise the lightbox AFTER all plugins are attached.
  myLightbox.init();
</script>

Base HTML pattern

HTML pattern (mirrors PhotoSwipe’s standard markup):

<div
  data-photoswipe-lightbox="my-gallery">
  <a
    href="big-1.jpg"
    data-pswp-width="1600" data-pswp-height="1067">
      <img
        src="thumb-1.jpg"
        alt="">
  </a>
  <a
    href="big-2.jpg" data-pswp-width="1600"
    data-pswp-height="1067">
      <img
        src="thumb-2.jpg"
        alt="">
  </a>
</div>

HTML pattern v1

The simplest way to combine PhotoSwipe with the Swiper slider is to put a <div> for every slide inside the slider’s wrapper element. Each slide contains the image and (optionally) a hidden caption element.

<!-- Slider main container (required) -->
<div id="gallery_id" class="swiper swiper-container">
  <!-- Slider wrapper element (required) -->
  <div class="swiper-wrapper">
    <!-- Slides (required) -->
    <div class="swiper-slide">
      <img src="path/to/image-1.jpg">
      <span class="pswp-caption-content">Caption: Image 1</span>
    </div>
    <!-- more slides -->
    <div class="swiper-slide">
      <img src="path/to/image-x.jpg">
      <span class="pswp-caption-content">Caption: Image X</span>
    </div>
  </div> <!-- END wrapper element -->
  <!-- Swiper Pagination (optional) -->
  <div class="swiper-pagination"></div>
  <!-- Swiper Navigation (optional) -->
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>
  <!-- Swiper Scrollbar (optional)-->
  <div class="swiper-scrollbar"></div>
</div> <!-- END Main container -->

HTML pattern v2

This variant uses an unordered list (<ul>/<li>) instead of plain <div> elements for the slides. Functionally it is identical to v1; use whichever markup fits your site best.

<!-- Slider main container (required) -->
<div id="gallery_id" class="swiper swiper-container">
  <!-- Slider wrapper element (required) -->
    <ul class="swiper-wrapper">
      <!-- Slides (required) -->
      <li class="swiper-slide">
        <img src="path/to/image-1.jpg">
        <span class="pswp-caption-content">Caption: Image 1</span>
      </li>
      <!-- more slides -->
      <li class="swiper-slide">
        <img src="path/to/image-x.jpg">
        <span class="pswp-caption-content">Caption: Image X</span>
      </li>
    </ul> <!-- END swiper-wrapper (slides) -->
  <!-- Swiper Pagination (optional) -->
  <div class="swiper-pagination"></div>
  <!-- Swiper Navigation (optional) -->
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>
  <!-- Swiper Scrollbar (optional)-->
  <div class="swiper-scrollbar"></div>
</div> <!-- END Main container -->

Extended HTML pattern

The extended layout wraps each thumbnail image in an <a> tag that links to the full-size image. The link also carries data-pswp-width and data-pswp-height attributes so PhotoSwipe knows the natural size of the full image before loading it. This avoids visible layout jumps and is required for the opening animation to work smoothly.

<!-- Slider main container (required) -->
<div id="gallery_id" class="swiper swiper-container">
  <!-- Slider wrapper element (required) -->
  <ul class="swiper-wrapper">
    <!-- Slides (required) -->
    <li class="swiper-slide">
      <a href="path/to/image-1.jpg"
        data-pswp-width="<max_width>"
        data-pswp-height="<max_height>">
        <img src="path/to/image-1.jpg" alt="Image 1">
        <span class="pswp-caption-content">Caption: Image 1</span>
      </a>
    </li>
    <!-- more slides -->
    <li class="swiper-slide">
      <a href="path/to/image-x.jpg"
        data-pswp-width="<max_width>"
        data-pswp-height="<max_height>">
        <img src="path/to/image-x.jpg" alt="Image X">
        <span class="pswp-caption-content">Caption: Image X</span>
      </a>
    </li>
  </ul> <!-- END swiper-wrapper (slides) -->
  <!-- Swiper Pagination (optional) -->
  <div class="swiper-pagination"></div>
  <!-- Swiper Navigation (optional) -->
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>
  <!-- Swiper Scrollbar (optional)-->
  <div class="swiper-scrollbar"></div>
</div> <!-- END Main container -->

Preload first slide

Boolean option preloadFirstSlide, default true. When enabled, PhotoSwipe starts downloading the image of the first slide in parallel with the opening animation. This makes the opening feel faster because the image is often ready by the time the animation finishes. Set the option to false if you would rather start the download only after the dialog is fully open.

import PhotoSwipeLightbox from '/photoswipe/photoswipe-lightbox.esm.js';
const lightbox = new PhotoSwipeLightbox({
  gallery:           '#gallery_id',
  pswpModule:        PhotoSwipe,
  children:          'a',
  preloadFirstSlide: false
});
lightbox.init();

Translating

PhotoSwipe shows a few short pieces of text in its user interface (button tooltips, error messages and the slide-counter separator). You can translate them into any language by overriding the options listed below.

{
  closeTitle:        'Close',          // tooltip on the close button
  zoomTitle:         'Zoom',           // tooltip on the zoom button
  arrowPrevTitle:    'Previous',       // tooltip on the previous-arrow button
  arrowNextTitle:    'Next',           // tooltip on the next-arrow button
  errorMsg:          'The image cannot be loaded',
  indexIndicatorSep: '/'               // separator in "1 / 10"
}
Example
const lightbox = new PhotoSwipeLightbox({
  gallery:           '#gallery_id',
  pswpModule:        PhotoSwipe,
  closeTitle:        'Close the dialog',
  zoomTitle:         'Zoom the photo',
  arrowPrevTitle:    'Go to the previous photo',
  arrowNextTitle:    'Go to the next photo',
  errorMsg:          'The photo cannot be loaded',
  indexIndicatorSep: ' of ',
  preloadFirstSlide: false
});
lightbox.init();

Auto initialization

Adds Lightbox V3-style auto-initialization to PhotoSwipeLightbox so that any element matching the configurable selector (default [data-photoswipe-lightbox]) is treated as a gallery container without requiring callers to instantiate new PhotoSwipeLightbox(…​) and .init() themselves.

Example

A view of a town with mountains in the background A view of a street with a lot of palm trees Red and white bridge over the trees A view of a city from a hill above the clouds

HTML pattern

<div
  data-photoswipe-lightbox="auto-gallery">
  <a
    href="big-1.jpg"
    data-pswp-width="1600" data-pswp-height="1067">
    <img
      src="thumb-1.jpg"
      alt="caption text 1">
  </a>
  <a
    href="big-2.jpg"
    data-pswp-width="1600" data-pswp-height="1067">
    <img
      src="thumb-2.jpg"
      alt="caption text 2">
  </a>
</div>

The element carrying data-photoswipe-lightbox (auto-gallery) is the gallery. Its descendant <a> tags are the slides. The attribute value is treated purely as a logical id (useful for getAutoInitInstance lookups) and is not required.

Parameters

A list of all available options (parameters) in alphabetical order. You can also find the original option settings in the PhotoSwipe Documentation.

allowPanToNext

Boolean, true. Allow swipe navigation to the next slide when the current slide is zoomed. Does not apply to mouse events.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  allowPanToNext: false
});
lightbox.init();

arrowKeys

Boolean, true. Use the left and right arrow keys to move between slides.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  arrowKeys: false
});
lightbox.init();

bgOpacity

Number, 0.8. Sets how transparent the dark backdrop behind the image is. A value of 0 is fully transparent, 1 is fully opaque. The backdrop’s color is set in CSS via --pswp-bg; only its opacity should be set here.

Name Type Default Description | Example

bgOpacity

number

0.8

Controls how transparent the backdrop behind the image is.

const lightbox = new PhotoSwipeLightbox({
  // ... other options
  bgOpacity: 0.2,
});
lightbox.init();

clickAction

Configures what happens when the user clicks on the image (imageClickAction) or on the dark background around it (bgClickAction). Possible values include 'close', 'next', 'zoom', 'zoom-or-close' and false (no action).

Refer to the click and tap actions page for the full list and examples.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  imageClickAction: 'zoom-or-close',
  bgClickAction:    'close'
});
lightbox.init();

clickToCloseNonZoomable

Boolean, true. If an image cannot be zoomed (for example, because it is smaller than the viewport), clicking on it will close the lightbox.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  clickToCloseNonZoomable: false
});
lightbox.init();

closeOnVerticalDrag

Boolean, true. Close the lightbox by dragging the image up or down (touch devices).

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  closeOnVerticalDrag: false
});
lightbox.init();

easing

String, 'cubic-bezier(.4,0,.22,1)'. Sets the CSS easing function used for the open, close and zoom transitions. Any valid CSS transition-timing-function value is accepted.

Find an Example here.

escKey

Boolean, true. Allow the user to close the lightbox by pressing the Esc key.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  escKey: false
});
lightbox.init();

errorMsg

String, 'The image cannot be loaded'.

The text that is shown when an image fails to load. If you need to display formatted HTML instead of plain text, use the contentErrorElement filter.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  errorMsg: 'The photo cannot be loaded'
});
lightbox.init();

getViewportSizeFn

Function, undefined. A function that returns the size of the slide’s viewport in pixels. The function receives the merged options object and the PhotoSwipe instance and must return an object of the form { x: <width>, y: <height> }. Use this when the lightbox is rendered inside a container that is smaller than the browser window.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  getViewportSizeFn: function(options, pswp) {
    return {
      x: document.documentElement.clientWidth - 200,
      y: window.innerHeight
    };
  }
});
lightbox.init();

hideAnimationDuration

Number, 333. Closing transition duration in milliseconds. Set to 0 to disable the closing animation.

Find an Example here.

You can use the options showAnimationDuration and hideAnimationDuration together (both are integers, default 333).

The easing option (string, default cubic-bezier(.4,0,.22,1)) accepts any CSS timing function and applies to every zoom transition (including double-tap).

Both options can also be changed dynamically while PhotoSwipe is open.

In the example below the transition duration is set to 1000 ms (1 s). The easing is changed dynamically: the opening transition uses ease-out-back, the zoom transitions use ease-in-out-back and the closing transition uses ease-in-back:

const backEasing = {
  in:    'cubic-bezier(0.6, -0.28, 0.7, 1)',
  out:   'cubic-bezier(0.3, 0, 0.32, 1.275)',
  inOut: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)'
};
const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  showHideAnimationType: 'zoom',
  showAnimationDuration: 1000,
  hideAnimationDuration: 1000
});
lightbox.on('firstUpdate', () => {
  lightbox.pswp.options.easing = backEasing.out;
});
lightbox.on('initialZoomInEnd', () => {
  lightbox.pswp.options.easing = backEasing.inOut;
});
lightbox.on('close', () => {
  lightbox.pswp.options.easing = backEasing.in;
});
lightbox.init();

indexIndicatorSep

String, ' / '. The separator used by the slide counter in the top toolbar. The default produces strings like 1 / 10. Use ' of ' to get 1 of 10 instead.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  indexIndicatorSep: ' of '
});
lightbox.init();

loop

Boolean, true. When enabled, swiping past the last slide takes the user back to the first slide (and vice versa). The option is always treated as false when the gallery has fewer than three slides.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  loop: false
});
lightbox.init();

mainClass

String, undefined. One or more CSS class names (separated by a space) that are added to the root element of PhotoSwipe. Use this to apply custom styles to a single lightbox instance. An example can be found on the Styling page.

appendToEl

DOM element, document.body. The element that PhotoSwipe will be appended to when it opens. Useful when the page uses a custom layout container (for example a documentation framework) that should host the lightbox instead of <body>.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  appendToEl: document.querySelector('#__docusaurus')
});
lightbox.init();

maxWidthToAnimate

Integer, 4000. Maximum width (in pixels) of an image that should be animated when opening or closing the lightbox. If the rendered image is wider than this value, the open/close animation is automatically disabled to keep the transition smooth.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  maxWidthToAnimate: 800,
});
lightbox.init();

padding

Object, { top: 0, bottom: 0, left: 0, right: 0 }. Adds inner padding (in pixels) around the slide area on each side. Useful for keeping content (such as captions or toolbars) away from the edges of the viewport.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  padding: { top: 20, bottom: 40, left: 100, right: 100 }
});
lightbox.init();

paddingFn

Function, undefined. A function that returns a padding object (same shape as padding). Overrides the padding option when set. The function is called frequently, so keep it simple and fast. The function receives the current viewport size, the data of the current slide and the slide index.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  paddingFn: (viewportSize, itemData, index) => {
    return {
      // value depends on the slide index
      top: index === 0 ? 100 : 0,
      // value depends on the viewport size
      bottom: viewportSize.x < 600 ? 0 : 200,
      // value depends on the image size
      left: itemData.w < 2000 ? 50 : 0,
      right: 0
    };
  }
});
lightbox.init();

pinchToClose

Boolean, true. Allow the user to close the lightbox with a pinch-out touch gesture.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  pinchToClose: false
});
lightbox.init();

preload

Array, [1, 2]. Preload nearby slides based on the user’s direction of movement. The first number is how many images to preload before the current one, the second is how many to preload after it. The two neighbouring images are always loaded.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  preload: [1, 4]
});
lightbox.init();

preloaderDelay

Number (ms), 2000. Delay (in milliseconds) before the spinning loading indicator appears. If the image finishes loading before this delay is up, the indicator never shows. Use 0 to display the indicator immediately.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  preloaderDelay: 0
});
lightbox.init();

returnFocus

Boolean, true. After the lightbox is closed, move the keyboard focus back to the element that had it before the lightbox opened. This is important for accessibility.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  returnFocus: false
});
lightbox.init();

showAnimationDuration

Number, 333. Opening transition duration in milliseconds. Set to 0 to disable the opening animation.

Find an Example here.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  showAnimationDuration: 1000
});
lightbox.init();

showHideAnimationType

String, "zoom". Adjust opening or closing transition. It supports three values:

  • zoom (default)

  • fade (default if there is no thumbnail)

  • none

Animations are automatically disabled, when user sets prefers-reduced-motion to reduce.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  showHideAnimationType: 'fade'
});
lightbox.init();

spacing

Number, 0.1. The gap between slides, expressed as a fraction of the viewport width. The default of 0.1 means a gap of 10 % of the viewport width.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  spacing: 0.5, // 50% of viewport width
});
lightbox.init();

trapFocus

Boolean, true. Keep the keyboard focus inside the lightbox while it is open. Together with returnFocus, this is the recommended setting for an accessible lightbox.

tapAction

Configures what happens when the user single-taps (tapAction) or double-taps (doubleTapAction) on a slide. Possible values include 'close', 'next', 'zoom', 'zoom-or-close' and false (no action).

Refer to the click and tap actions page for the full list and examples.

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  tapAction:       'toggle-controls',
  doubleTapAction: 'zoom'
});
lightbox.init();

wheelToZoom

Boolean, undefined. By default, PhotoSwipe zooms the image only when the user holds Ctrl and turns the mouse wheel. When this option is enabled, simply turning the wheel zooms the image (no Ctrl key required).

const lightbox = new PhotoSwipeLightbox({
  gallery: '#gallery_id',
  pswpModule: PhotoSwipe,
  children: 'a',
  wheelToZoom: true
});
lightbox.init();

zoomAnimationDuration

Number, 333. Duration (in milliseconds) of the zoom-in / zoom-out transition. Set to 0 to disable the zoom animation.

Find an Example here.

zoomLevel

There are three related options that control how far the user can zoom into an image:

  • initialZoomLevel – the zoom level applied when a slide first opens.

  • secondaryZoomLevel – the zoom level applied after a single zoom click or tap.

  • maxZoomLevel – the maximum zoom level the user can reach by pinching, double-tapping, or rolling the mouse wheel.

Each option accepts a number (e.g. 2 for 200 %), one of the strings 'fit', 'fill', 'auto', or a function that returns one of those values. See Adjusting zoom level for details. The default values are described there too.

Methods

init

init()

Initialise the lightbox. Must be called exactly once for each lightbox instance. This call does not open the dialog itself; it only attaches the click handlers that will open the dialog later (by default a normal mouse click on a thumbnail). It is also the place where PhotoSwipe will preload images that are needed before the dialog opens.

const lightbox = new PhotoSwipeLightbox({
  // options
});
// you may bind events here
lightbox.init();

destroy

destroy()

Removes every event listener that the lightbox has attached and closes the lightbox if it is currently open. After this call the instance can no longer be used. To reopen the lightbox you have to create a new PhotoSwipeLightbox and call init() again.

let lightbox = new PhotoSwipeLightbox({
  gallery:    '#gallery--test-destroy',
  pswpModule: PhotoSwipe,
  children:   'a',
  returnFocus: false
});
lightbox.init();
const button = document.createElement('button');
button.type = 'button';
button.innerText = 'destroy the photoswipe';
document.querySelector('#gallery--test-destroy').after(button);
button.onclick = () => {
  if (lightbox) {
    lightbox.destroy();
    lightbox = null;
  }
};

loadAndOpen

loadAndOpen(index, dataSource, point)

Programmatically open PhotoSwipe at a given slide. Use this when you need to open the lightbox from code (for example from a button click) instead of waiting for the user to click a thumbnail.

Arguments:

  1. index (number) – Zero-based index of the slide to open.

  2. dataSource (optional, DataSource) – The gallery whose slides should be shown. Use this only if you want to override the default gallery.

  3. point (optional, { x: number, y: number }) – Pixel coordinates of the click that opened the lightbox. By default this is { x: e.clientX, y: e.clientY } of the original click event.

If you use the gallery and children options, and you omit the second argument, PhotoSwipe will use the first matching gallery element. To open a specific gallery element instead, pass it as the data source: { gallery: HTMLElement }. For example:

const lightbox = new PhotoSwipeLightbox({
  gallery:    '#gallery--with-button',
  pswpModule: PhotoSwipe,
  children:   'a',
});
lightbox.init();
const btn = document.createElement('button');
btn.type = 'button';
btn.innerText = 'open second image';
document.querySelector('#gallery--with-button').after(btn);
btn.onclick = () => {
  lightbox.loadAndOpen(1, {
    gallery: document.querySelector('#gallery--with-button')
  });
  // Alternatively you can simply trigger the native click:
  // document.querySelector('#gallery--with-button a:nth-of-type(2)').click();
};

Events

PhotoSwipe Lightbox lets you react to a number of events. All event handlers can be attached directly to the lightbox instance with lightbox.on(eventName, callback). They are forwarded to the PhotoSwipe core automatically as soon as the dialog opens.

const lightbox = new PhotoSwipeLightbox({
  gallery:    '#gallery_id',
  pswpModule: PhotoSwipe,
  children:   'a'
});
lightbox.on('change', () => {
  // runs every time the user moves to another slide
  console.log('current index:', lightbox.pswp.currIndex);
});
lightbox.init();

Initialization events

These events fire while the lightbox is opening, in the order shown below:

beforeOpen

PhotoSwipe is about to open. Use it to prepare data before any UI is created.

firstUpdate

The DOM structure has been built and the initial slide index is set. You can change the initial index here.

initialLayout

PhotoSwipe is measuring sizes. If you need to read element dimensions with getBoundingClientRect, do it here.

change

A slide has become active. Fires both at first open and whenever the user navigates to another slide.

afterInit

PhotoSwipe is fully initialised; the opening transition is now running.

bindEvents

PhotoSwipe has finished binding its DOM event listeners (pointer events, wheel, keyboard, etc.).

const lightbox = new PhotoSwipeLightbox({
  gallery:    '#gallery--test-init-events',
  pswpModule: PhotoSwipe,
  children:   'a'
});
lightbox.on('beforeOpen', () => {
  console.log('beforeOpen');
});
lightbox.on('firstUpdate', () => {
  console.log('firstUpdate');
});
lightbox.on('initialLayout', () => {
  console.log('initialLayout');
});
lightbox.on('change', () => {
  console.log('change');
});
lightbox.on('afterInit', () => {
  console.log('afterInit');
});
lightbox.on('bindEvents', () => {
  console.log('bindEvents');
});
lightbox.init();

Opening and closing transitions

These events fire even when the corresponding transition is disabled (for example when showAnimationDuration is 0).

openingAnimationStart

The opening animation has just started.

openingAnimationEnd

The opening animation has finished. The lightbox is now fully open and ready to receive user input.

closingAnimationStart

The closing animation has just started.

closingAnimationEnd

The closing animation has finished. The lightbox is no longer visible.

const lightbox = new PhotoSwipeLightbox({
  gallery:    '#gallery--test-opening-closing-events',
  pswpModule: PhotoSwipe,
  children:   'a'
});
lightbox.on('openingAnimationStart', () => {
  console.log('openingAnimationStart');
});
lightbox.on('openingAnimationEnd', () => {
  console.log('openingAnimationEnd');
});
lightbox.on('closingAnimationStart', () => {
  console.log('closingAnimationStart');
});
lightbox.on('closingAnimationEnd', () => {
  console.log('closingAnimationEnd');
});
lightbox.init();

Closing

close

PhotoSwipe is starting to close. This is the right moment to unbind listeners that were added on open.

destroy

PhotoSwipe is fully closed and its DOM nodes are about to be removed. Tear down anything that should not outlive the lightbox here.

const lightbox = new PhotoSwipeLightbox({
  gallery:    '#gallery--test-closing-events',
  pswpModule: PhotoSwipe,
  children:   'a'
});
lightbox.on('close', () => {
  // PhotoSwipe starts to close - unbind most events here
  console.log('close');
});
lightbox.on('destroy', () => {
  // PhotoSwipe is fully closed - destroy everything
  console.log('destroy');
});
lightbox.init();

Pointer and gesture events

These events report low-level pointer (mouse, pen, touch) input.

pointerDown / pointerMove / pointerUp

Fire on the equivalent native pointer event. The original DOM event is available as e.originalEvent.

pinchClose

Fires while the user is closing the lightbox with a pinch gesture. Calling e.preventDefault() cancels the gesture.

verticalDrag

Fires while the user is closing the lightbox with a vertical drag. Calling e.preventDefault() cancels the gesture.

const lightbox = new PhotoSwipeLightbox({
  gallery:    '#gallery--test-pointer-events',
  pswpModule: PhotoSwipe,
  children:   'a'
});
lightbox.on('pointerDown', (e) => {
  console.log('pointerDown', e.originalEvent);
});
lightbox.on('pointerMove', (e) => {
  console.log('pointerMove', e.originalEvent);
});
lightbox.on('pointerUp', (e) => {
  console.log('pointerUp', e.originalEvent);
});
lightbox.on('pinchClose', (e) => {
  // triggered when using pinch to close gesture
  // can be prevented with e.preventDefault()
  console.log('pinchClose', e.bgOpacity);
});
lightbox.on('verticalDrag', (e) => {
  // triggered when using vertical drag to close gesture
  // can be prevented with e.preventDefault()
  console.log('verticalDrag', e.panY);
});
lightbox.init();

Slide content events

These events let you observe (and in many cases override) how slide content is loaded, resized and added to the page. Refer to the Custom Content section of the official docs for full examples.

contentInit

A new content object has just been created.

contentLoad

Content is about to start loading. Can be cancelled with e.preventDefault(). Useful when you want to provide a custom element on content.element.

contentLoadImage

Same as contentLoad, but only fires for image content.

loadComplete

Content has finished loading.

contentResize

Content is about to be resized. Can be cancelled.

imageSizeChange

The displayed size of an image content has changed (the image element is on content.element).

contentLazyLoad

Lazy loading of a piece of content is starting. Can be cancelled.

contentAppend

Content is about to be appended to the DOM. Can be cancelled if you want to do this yourself.

contentActivate / contentDeactivate

Fired when a piece of content becomes the current slide / stops being the current slide. Both can be cancelled.

contentRemove

Content is about to be removed from the DOM. Can be cancelled.

contentDestroy

Content is about to be destroyed. Can be cancelled.

const lightbox = new PhotoSwipeLightbox({
  gallery:    '#gallery--test-content-events',
  pswpModule: PhotoSwipe,
  children:   'a'
});
lightbox.on('contentInit', ({ content }) => {
  console.log('contentInit', content);
});
lightbox.on('contentLoad', ({ content, isLazy }) => {
  console.log('contentLoad', content, isLazy);
});
lightbox.on('contentLoadImage', ({ content, isLazy }) => {
  console.log('contentLoadImage', content, isLazy);
});
lightbox.on('loadComplete', ({ content, slide }) => {
  console.log('loadComplete', content);
});
lightbox.on('contentResize', ({ content, width, height }) => {
  console.log('contentResize', content, width, height);
});
lightbox.on('imageSizeChange', ({ content, width, height, slide }) => {
  console.log('imageSizeChange', content, width, height, slide, slide.index);
});
lightbox.on('contentLazyLoad', ({ content }) => {
  console.log('contentLazyLoad', content);
});
lightbox.on('contentAppend', ({ content }) => {
  console.log('contentAppend', content);
});
lightbox.on('contentActivate', ({ content }) => {
  console.log('contentActivate', content);
});
lightbox.on('contentDeactivate', ({ content }) => {
  console.log('contentDeactivate', content);
});
lightbox.on('contentRemove', ({ content }) => {
  console.log('contentRemove', content);
});
lightbox.on('contentDestroy', ({ content }) => {
  console.log('contentDestroy', content);
});
lightbox.init();

Captions

PhotoSwipe does not show captions out of the box, but a small plugin called PhotoSwipe Dynamic Caption adds that feature. The plugin automatically places the caption text either below or to the right of the image, depending on how much space is available. It works best for short to medium-length captions and only for images that use the default fit scale mode.

Plugin demo

For accessibility, make sure that important caption text is also available outside of PhotoSwipe – for example by setting an alt attribute on the thumbnail image or by linking the caption to the thumbnail with aria-labelledby.

Parameters

The Dynamic Caption plugin reads its caption text from a hidden element inside each slide. By default it looks for an element with the CSS class .pswp-caption-content and uses its inner HTML as the caption. If no such element is found, the plugin falls back to the alt attribute of the thumbnail image.

The minimum HTML that the plugin expects on each slide looks like this:

HTML Structure
<a href="path/to/large-image.jpg" data-pswp-width="1024" data-pswp-height="768">
  <img src="path/to/thumbnail.jpg" alt="" />
  <span class="pswp-caption-content">Caption content</span>
</a>

You can also build the caption text programmatically by passing a function as captionContent. The function receives the current slide object and must return a string of HTML (or plain text):

Example
captionContent: (slide) => {
  return slide.data.element.querySelector('img').getAttribute('alt');
}
Name Type Default Description | Example

captionContent

string | function

.pswp-caption-content

Where to read the caption text from. If a string is supplied it is used as a CSS selector and the plugin reads the inner HTML of the matching element. If the element is not found, the plugin uses the alt attribute of the thumbnail image. A function may be used instead of a selector to build the caption from any data you like.

type

string

auto

Position of the caption. Allowed values are auto, below or aside:

  • below – the caption is always shown below the image.

  • aside – the caption is always shown to the right of the image.

  • auto – the plugin picks the better of the two depending on how much space is available.

mobileLayoutBreakpoint

number | function

600

Window width (in pixels) below which the mobile caption layout is used. May also be a function that returns true to force the mobile layout.

Example
mobileLayoutBreakpoint: (pswp, captionPlugin) => {
  return (window.innerWidth < 750);
}

horizontalEdgeThreshold

number

20

When the caption’s x position is smaller than this value (in pixels), the plugin adds the class pswp__dynamic-caption—​on-hor-edge to the caption element. Use this hook to apply a different style (for example extra horizontal padding) when the caption sits at the very edge of the screen.

mobileCaptionOverlapRatio

number

0.3

Controls when the mobile caption switches to an overlap layout. The value is a ratio between 0 and 1. With the default 0.3, the caption begins overlapping the image once more than 30 % of the horizontal space is empty. Set the value to 0 to make the caption always overlap, or to 1 to make it never overlap (the image will always shift up to make room, unless it is taller than the viewport).

verticallyCenterImage

boolean

false

When set to true, the image is always vertically centred in the remaining space between the caption and the rest of the viewport. With the default value false, the image is only lifted up enough to make room for the caption when it does not fit underneath.

Styling

The caption element always has the CSS class pswp__dynamic-caption. Depending on its current position the plugin adds one of the following modifier classes:

  • pswp__dynamic-caption—​below – caption is below the image.

  • pswp__dynamic-caption—​aside – caption is to the right of the image.

  • pswp__dynamic-caption—​mobile – caption is pinned to the bottom of the viewport (the default mobile layout).

If the caption sits near the left horizontal edge it additionally gets the class pswp__dynamic-caption—​on-hor-edge.

You can adjust the look of the captions in the plugin CSS file (media queries are welcome):

.pswp__dynamic-caption--aside {
  max-width: 300px;
  padding: 20px 15px 20px 20px;
  margin-top: 70px;
}
.pswp__dynamic-caption--below {
  max-width: 700px;
  padding: 15px 0 0;
}
.pswp__dynamic-caption--mobile {
  background: rgba(0, 0, 0, 0.5);
  padding: 10px 15px;
}

Internally, the plugin decides where to place the caption like this:

  • It compares the amount of free horizontal space and free vertical space around the image.

  • If there is more vertical space:

    • The caption width is set to the width of the image.

    • The class pswp__dynamic-caption—​below is added so its size can be adjusted via CSS.

    • The caption height is measured.

    • If the caption fits without moving the image, it is shown right below the image.

    • If it does not fit, the pan area height is reduced by the height of the caption to make room.

  • If there is more horizontal space:

    • The class pswp__dynamic-caption—​aside is added so its size can be adjusted via CSS.

    • The caption width is measured.

    • If the caption fits next to the image without moving it, it is shown on the right side.

    • If it does not fit, the pan area width is reduced by the caption width to make room.

If the mobileLayoutBreakpoint is met:

  • The caption height is measured at 100 % of the available width.

  • The pan area height is reduced so the caption fits below the image.

  • The remaining horizontal space is checked.

  • If too much horizontal space is left over (see mobileCaptionOverlapRatio), the caption simply overlaps the image and the image is left at its default position.