Snippets Gallery

Bootstrap 5 Photo Gallery with Modal Lightbox

A bento-style photo gallery with a featured tile, caption overlays and a modal lightbox with previous and next controls, arrow-key support and a photo counter.

Bootstrap 5 No jQuery Mar 2020 5.9K views

<section class="wdw-stage gl-stage">
  <div class="container" style="max-width:1000px">
    <div class="d-flex flex-column flex-sm-row justify-content-between align-items-sm-end gap-2 mb-4">
      <div><span class="wdw-eyebrow mb-1">Gallery</span><h2 class="wdw-heading h2 mb-0">Good dogs, 2024</h2></div>
      <p class="wdw-muted mb-0">Select any photo to view it full size.</p>
    </div>
    <div class="gl-grid">
      <button type="button" class="gl-card gl-feat" aria-haspopup="dialog" data-bs-toggle="modal" data-bs-target="#gl-modal" data-src="https://images.unsplash.com/photo-1544568100-847a948585b9?auto=format&fit=crop&w=1600&q=80" data-title="The good ol' boy" data-sub="Trail walk, 2024">
        <img src="https://images.unsplash.com/photo-1544568100-847a948585b9?auto=format&fit=crop&w=1000&h=800&q=75" width="1000" height="800" loading="lazy" alt="The good ol' boy" class="gl-img">
        <span class="gl-scrim"><span class="gl-cap"><span class="d-block fw-semibold">The good ol' boy</span><span class="d-block small gl-sub">Trail walk, 2024</span></span><span class="gl-open">Open photo</span></span>
      </button>
      <button type="button" class="gl-card" aria-haspopup="dialog" data-bs-toggle="modal" data-bs-target="#gl-modal" data-src="https://images.unsplash.com/photo-1517423440428-a5a00ad493e8?auto=format&fit=crop&w=1600&q=80" data-title="Mr I didn't do it" data-sub="Studio portrait">
        <img src="https://images.unsplash.com/photo-1517423440428-a5a00ad493e8?auto=format&fit=crop&w=600&h=600&q=75" width="600" height="600" loading="lazy" alt="Mr I didn't do it" class="gl-img">
        <span class="gl-scrim"><span class="gl-cap"><span class="d-block fw-semibold">Mr I didn't do it</span><span class="d-block small gl-sub">Studio portrait</span></span><span class="gl-open">Open photo</span></span>
      </button>
      <button type="button" class="gl-card" aria-haspopup="dialog" data-bs-toggle="modal" data-bs-target="#gl-modal" data-src="https://images.unsplash.com/photo-1510771463146-e89e6e86560e?auto=format&fit=crop&w=1600&q=80" data-title="Spring sniff" data-sub="Garden">
        <img src="https://images.unsplash.com/photo-1510771463146-e89e6e86560e?auto=format&fit=crop&w=600&h=600&q=75" width="600" height="600" loading="lazy" alt="Spring sniff" class="gl-img">
        <span class="gl-scrim"><span class="gl-cap"><span class="d-block fw-semibold">Spring sniff</span><span class="d-block small gl-sub">Garden</span></span><span class="gl-open">Open photo</span></span>
      </button>
      <button type="button" class="gl-card" aria-haspopup="dialog" data-bs-toggle="modal" data-bs-target="#gl-modal" data-src="https://images.unsplash.com/photo-1507146426996-ef05306b995a?auto=format&fit=crop&w=1600&q=80" data-title="Sunny puppy" data-sub="First summer">
        <img src="https://images.unsplash.com/photo-1507146426996-ef05306b995a?auto=format&fit=crop&w=600&h=600&q=75" width="600" height="600" loading="lazy" alt="Sunny puppy" class="gl-img">
        <span class="gl-scrim"><span class="gl-cap"><span class="d-block fw-semibold">Sunny puppy</span><span class="d-block small gl-sub">First summer</span></span><span class="gl-open">Open photo</span></span>
      </button>
      <button type="button" class="gl-card" aria-haspopup="dialog" data-bs-toggle="modal" data-bs-target="#gl-modal" data-src="https://images.unsplash.com/photo-1530281700549-e82e7bf110d6?auto=format&fit=crop&w=1600&q=80" data-title="Wide smile" data-sub="Park day">
        <img src="https://images.unsplash.com/photo-1530281700549-e82e7bf110d6?auto=format&fit=crop&w=600&h=600&q=75" width="600" height="600" loading="lazy" alt="Wide smile" class="gl-img">
        <span class="gl-scrim"><span class="gl-cap"><span class="d-block fw-semibold">Wide smile</span><span class="d-block small gl-sub">Park day</span></span><span class="gl-open">Open photo</span></span>
      </button>
      <button type="button" class="gl-card gl-wide" aria-haspopup="dialog" data-bs-toggle="modal" data-bs-target="#gl-modal" data-src="https://images.unsplash.com/photo-1548199973-03cce0bbc87b?auto=format&fit=crop&w=1600&q=80" data-title="Best friends" data-sub="Beach run">
        <img src="https://images.unsplash.com/photo-1548199973-03cce0bbc87b?auto=format&fit=crop&w=900&h=450&q=75" width="900" height="450" loading="lazy" alt="Best friends" class="gl-img">
        <span class="gl-scrim"><span class="gl-cap"><span class="d-block fw-semibold">Best friends</span><span class="d-block small gl-sub">Beach run</span></span><span class="gl-open">Open photo</span></span>
      </button>
    </div>
  </div>
</section>
<div class="modal fade" id="gl-modal" tabindex="-1" aria-labelledby="gl-title" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered modal-xl modal-fullscreen-md-down">
    <div class="modal-content gl-modal-content overflow-hidden border-0">
      <button type="button" class="btn-close btn-close-white gl-close" data-bs-dismiss="modal" aria-label="Close"></button>
      <div class="gl-stagebox position-relative">
        <div id="gl-big" class="d-flex justify-content-center w-100"></div>
        <button type="button" class="gl-nav gl-prev" id="gl-prev" aria-label="Previous photo"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 5l-7 7 7 7"/></svg></button>
        <button type="button" class="gl-nav gl-next" id="gl-next" aria-label="Next photo"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 5l7 7-7 7"/></svg></button>
      </div>
      <div class="d-flex justify-content-between align-items-center gap-3 px-4 py-3">
        <h3 class="h6 fw-bold mb-0" id="gl-title">Photo</h3>
        <span class="small gl-count" id="gl-count" aria-live="polite">1 / 6</span>
      </div>
    </div>
  </div>
</div>

Built on Bootstrap 5.3.8 with plain JavaScript — no jQuery. Supports light and dark mode via data-bs-theme.

Free to use in personal and commercial projects under the MIT license.