Some of the posts that you see in this blog feature an image gallery. If you want to see a working example, there is one in the post about the Portuguese National Tile Museum.

That gallery is using Photoswipe and simple css for the mansonry effect.

If you want to replicate, here’s how it works.

1. Set frontmatter


We are using the shortversion, where the name and title and simply derived from the number of images in the folder.

2. shortcodes/gallery.html

The shortcode is used in the following format:

{{< gallery folder="gallery" title="Escher in Lisbon" >}}

The shortcode’s logic is simple. First, use .Page.Resources.Match to get all the resources whose name matches the gallery folder string. It’s important the resource name starts with the folder’s name.

{{ with .Get "title" }}
  <h4>{{ . }}</h4>
{{ end }}
  <div class="gallery" itemscope itemtype="">
  {{- range (.Page.Resources.Match (printf "%s*" (.Get "folder"))) }}
  {{ $thumbnail := .Resize "320x" }}
    <figure itemscope itemtype="" class="image gallery-item">
    <a href="{{ .Permalink }}" itemprop="contentUrl" data-size="{{ .Width }}x{{ .Height }}" >
      <img src="{{ $thumbnail.Permalink }}" itemprop="thumbnail" alt="galleryImage" class="galleryImage" />
    <figcaption itemprop="caption description">
      <span itemprop="copyrightHolder"></span>
  {{ end }}

No gimmicks here, just the standard photoswipe html and making sure it is required using .HasShortcode.

{{- if .HasShortcode "gallery" -}}
  <!-- Root element of PhotoSwipe. Must have class pswp. -->
  <div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
  <!-- Background of PhotoSwipe.
     It's a separate element, as animating opacity is faster than rgba(). -->
  <div class="pswp__bg"></div>
  <!-- Slides wrapper with overflow:hidden. -->
  <div class="pswp__scroll-wrap">
    <!-- Container that holds slides.
      PhotoSwipe keeps only 3 of them in DOM to save memory.
      Don't modify these 3 pswp__item elements, data is added later on. -->
    <div class="pswp__container">
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
    <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
    <div class="pswp__ui pswp__ui--hidden">
    <div class="pswp__top-bar">
      <!--  Controls are self-explanatory. Order can be changed. -->
      <div class="pswp__counter"></div>
      <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
      <button class="pswp__button pswp__button--share" title="Share"></button>
      <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
      <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
      <!-- Preloader demo -->
      <!-- element will get class pswp__preloader--active when preloader is running -->
      <div class="pswp__preloader">
      <div class="pswp__preloader__icn">
        <div class="pswp__preloader__cut">
        <div class="pswp__preloader__donut"></div>
    <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
      <div class="pswp__share-tooltip"></div>
    <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
    <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
    <div class="pswp__caption">
      <div class="pswp__caption__center"></div>
  <link rel="stylesheet" href="/plugins/photoswipe/photoswipe.css">
  <link rel="stylesheet" href="/plugins/photoswipe/default-skin/default-skin.css">

  <script src="/plugins/photoswipe/photoswipe.min.js"></script>
  <script src="/plugins/photoswipe/photoswipe-ui-default.min.js"></script>
  <script src="/plugins/photoswipe/initphotoswipe.js"></script>
{{- end -}}

4. CSS

I am using the Photoswipe’s CSS and columns to display thumbnails:


  width: 100%;
  display: block;
  margin:0 auto;

/* Extra Small Devices, Phones */ 
@media only screen and (min-width : 480px) {
  .gallery {
    column-count: 1;
    column-gap: 3px;
/* Small Devices, Tablets */
@media only screen and (min-width : 768px) {
  .gallery {
    column-count: 3;
    column-gap: 3px;

/* Medium Devices, Desktops */
@media only screen and (min-width : 992px) {
  .gallery { /* Masonry container */
      column-count: 4;
      column-gap: 3px;

/* Large Devices, Wide Screens */ 
@media only screen and (min-width : 1200px) {
    column-count: 5;
    column-gap: 0.2rem;
    width: 100%;

.gallery-item { /* Masonry bricks or child elements */
    background-color: #eee;
    display: inline-block;
    margin: 0 0 0.2rem;
    width: auto;

5. And that’s it. Hope it helps!

Unsplash Logosoragrit wongsa

avatar Bruno Amaral
Bruno Amaral

I am a Digital Strategist, divided between tech and creativity, working for the Lisbon Collective and teaching Public Relations at the …