Create a Fast Lightbox Gallery with PhotoSwipe 5 or GLightbox

We've seen it happen: a click on a thumbnail opens a blank screen, or the gallery stutters on mobile. Common mistakes include no lazy loading, ignoring touch events, and incorrect preloading. As a result, LCP suffers and users leave. Let's dive into how to implement a fast, user-friendly lightbox us

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1414
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    982
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1241
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Website development for FIXPER company
    995

We've seen it happen: a click on a thumbnail opens a blank screen, or the gallery stutters on mobile. Common mistakes include no lazy loading, ignoring touch events, and incorrect preloading. As a result, LCP suffers and users leave. Let's dive into how to implement a fast, user-friendly lightbox using PhotoSwipe 5 or GLightbox with SEO and accessibility in mind. With 5+ years of experience and over 200 gallery implementations, we deliver reliable solutions. Contact us for a professional implementation—we'll choose the optimal solution for your stack.

Why PhotoSwipe 5 is the Best Choice for Production

Criteria PhotoSwipe 5 GLightbox Fancybox 5
Size (gzip) ~20 KB ~12 KB ~30 KB
Touch swipes Built-in Built-in Paid
Zoom Yes No Yes
Video No Yes Yes
Price Free Free Paid
srcset support Full Limited Full

PhotoSwipe 5 wins on performance and accessibility. It's written in vanilla JS with no framework dependencies. For simple cases without zoom and video, GLightbox works, but for photo galleries with responsive design and mobile users, the choice is clear. Our experience shows that PhotoSwipe 5 gives the best control over Core Web Vitals. PhotoSwipe documentation recommends it for resource-intensive galleries.

Ensuring Accessibility and Performance

ARIA and Keyboard. PhotoSwipe 5 natively supports keyboard navigation: Tab, Enter, arrows, Esc. Additionally, we return focus to the element that opened the gallery:

lightbox.on('close', () => { document.querySelector('[data-gallery-opener]')?.focus(); }); 

For the grid, use role="list" and aria-label="Project gallery". Each item is <li role="listitem">. This is critical for screen readers.

Alt texts. Each image gets a meaningful alt containing keywords (e.g., "Lightbox gallery example with PhotoSwipe"). This improves SEO and accessibility.

Lazy Loading. Lazy loading thumbnails and preloading adjacent full-size images are the foundation of a fast gallery. Use the loading="lazy" attribute for thumbnails and the preload config in PhotoSwipe. This reduces LCP and saves bandwidth. For retina displays, add srcset—without it, images lose sharpness. See the srcset example below. PhotoSwipe 5 loads the first image 1.5 times faster than Fancybox 5, and GLightbox is 40% faster for simple galleries.

Integration Examples

Start with installing the package: npm install photoswipe. Import styles: import 'photoswipe/style.css'. Initialize the lightbox with preload and zoom settings. Add data attributes to full-size links. Set up event handlers for closing and focus return.

PhotoSwipe Integration in React

import PhotoSwipeLightbox from 'photoswipe/lightbox'; import 'photoswipe/style.css'; import { useEffect, useRef } from 'react'; interface GalleryImage { src: string; thumbnail: string; width: number; height: number; alt: string; caption?: string; } export function PhotoGallery({ images, id = 'gallery' }: { images: GalleryImage[]; id?: string }) { const galleryRef = useRef<HTMLElement>(null); useEffect(() => { if (!galleryRef.current) return; const lightbox = new PhotoSwipeLightbox({ gallery: `#${id}`, children: 'a', pswpModule: () => import('photoswipe'), preload: [1, 2], showHideAnimationType: 'zoom', closeOnVerticalDrag: true, maxZoomLevel: 4, initialZoomLevel: 'fit', secondaryZoomLevel: 1.5, }); lightbox.on('uiRegister', () => { lightbox.pswp?.ui?.registerElement({ name: 'custom-caption', order: 9, isButton: false, appendTo: 'root', html: '<div class="pswp__custom-caption"></div>', onInit: (el, pswp) => { pswp.on('change', () => { const currSlideElement = pswp.currSlide?.data.element; const caption = currSlideElement?.querySelector('figcaption')?.textContent ?? ''; el.querySelector('.pswp__custom-caption')!.textContent = caption; }); }, }); }); lightbox.init(); return () => lightbox.destroy(); }, [id, images]); return ( <section id={id} ref={galleryRef as any} className="photo-gallery" aria-label="Photo gallery"> {images.map((img, i) => ( <figure key={i} className="photo-gallery__item"> <a href={img.src} data-pswp-width={img.width} data-pswp-height={img.height}> <img src={img.thumbnail} alt={img.alt} loading="lazy" decoding="async" width={img.width} height={img.height} /> </a> {img.caption && <figcaption>{img.caption}</figcaption>} </figure> ))} </section> ); } 

Adaptive Images with srcset

function buildSrcSet(baseUrl: string, sizes: number[]): string { return sizes.map(w => `${baseUrl}?w=${w} ${w}w`).join(', '); } const dataSource = images.map(img => ({ src: img.src, srcset: buildSrcSet(img.src, [800, 1200, 1920, 2560]), width: img.width, height: img.height, alt: img.alt, msrc: img.thumbnail, })); 

Lazy Loading with Placeholder

import { useState } from 'react'; function LazyGalleryImage({ src, thumbnail, alt, width, height }: GalleryImage) { const [loaded, setLoaded] = useState(false); const [error, setError] = useState(false); return ( <div className={`gallery-image ${loaded ? 'gallery-image--loaded' : ''}`}> {!loaded && !error && ( <div className="gallery-image__placeholder" style={{ paddingBottom: `${(height / width * 100).toFixed(2)}%` }} /> )} <img src={thumbnail} data-full-src={src} alt={alt} loading="lazy" decoding="async" onLoad={() => setLoaded(true)} onError={() => setError(true)} style={{ opacity: loaded ? 1 : 0, transition: 'opacity 0.3s' }} /> </div> ); } 
Full Vue 3 Example with GLightbox
<template> <div ref="galleryRef" class="gallery"> <a v-for="img in images" :key="img.src" :href="img.src" :data-type="img.type"> <img :src="img.thumbnail" :alt="img.alt" loading="lazy" /> </a> </div> </template> <script setup> import GLightbox from 'glightbox'; import 'glightbox/dist/css/glightbox.css'; import { onMounted, ref } from 'vue'; const props = defineProps({ images: Array }); const galleryRef = ref(null); onMounted(() => { GLightbox({ gallery: galleryRef.value, autoplayVideos: false, touchNavigation: true, loop: false, }); }); </script> 

Practical Considerations

Timeline and Cost

Basic GLightbox integration with CSS columns—from $500 (0.5 day). PhotoSwipe with masonry, srcset, caption, and custom UI—$1500 (1.5–2 days). Full-featured gallery with API loading, pagination, filters, and video—$3000 (4–5 days). Cost is calculated individually—we'll assess after a brief. Order a comprehensive implementation and get a ready-made solution.

Common Beginner Mistakes

  • No lazy loading: all full-size images load at once, LCP suffers.
  • Ignoring srcset: pixelation on retina screens.
  • No focus return: screen readers get lost after closing.
  • Forgetting preload for neighbors: click leads to long load.

Why Choose Us

Over the years, we've implemented lightboxes in dozens of projects of varying complexity—from e-commerce stores to stock photo sites. We use up-to-date libraries, optimize Core Web Vitals, and guarantee quality. Get a consultation—we'll pick the optimal solution for your stack.

PhotoSwipe on GitHub · Lazy loading on MDN