Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Creating an Image Gallery with Hugo and Lightbox2

5.00/5 (2 votes)
4 Jun 2024CPOL4 min read 11.3K  
How to create an image gallery with Lightbox2
In this article, you will learn to create an image gallery with Lightbox2 and Hugo.

In the last few years, I built multiple static sites with Jekyll. Altogether, I’m happy with Jekyll, but there are a few things that could be better, with the build duration being on top of the list.

I wanted to evaluate another static site generator as an alternative, and I picked Hugo mainly because of its speed (I read multiple sources that said it’s really fast).

Compared to Jekyll, I find Hugo’s learning curve insanely steep, though…and it took me quite some time to grasp enough concepts until I was able to create a basic site.

At that point, I decided to create an image gallery first. That was one of my first experiments with Jekyll - after getting the basics to run, I built an image gallery with Lightbox2 in two versions (1, 2).

Here’s how to create the same result with Hugo.

Directory Structure

Example code is here in the code-examples repo. While explaining the steps, I will link to the specific commit where I actually did the respective step.

This is Hugo’s basic structure for the content directory:

|       
+---content
|   \---galleries
|       |   _index.md
|       |   
|       \---gallery1
|           |   index.md
|           |   
|           \---img
|                   esmeralda.jpg
|                   notebook.jpg
|                   Pope-Edouard-de-Beaumont-1844.jpg
|                   Victor_Hugo-Hunchback.jpg

Note the different _index.md and index.md:

  • _index.md is an index page, which just list its sub-items.
  • index.md (without underscore) is a “single page”, which means an actual content page.

Here’s the commit where I create the project with the .md files. Plus, I needed some sample images for the gallery, so I just copied some from the example site provided with the Ananke theme.

Note the destination directory: the images are in a subdirectory of /content/galleries/gallery1, so they are part of /content/galleries/gallery1/index.md’s page bundle. In Hugo terminology, the images are page resources, which means that we can list the current page’s images like this:

HTML
{{ with .Page.Resources.ByType "image" }}
    <ul>
    {{ range . }}
    <li>{{ .Permalink }}</li>
    {{ end }}
    </ul>
{{ end }}

But apparently, it’s not possible in Hugo to paste that code directly into the page for testing…so we need to create a shortcode first:

Shortcodes

For those who know Jekyll, Jekyll’s includes are called shortcodes in Hugo.

Shortcodes allow us to create things we need multiple times (like the code to show an image gallery :-) in a central HTML file - in this case, it’s the “list all images” code from the last paragraph:

/layouts/shortcodes/gallery.html:

HTML
{{ with .Page.Resources.ByType "image" }}
    <ul>
    {{ range . }}
    <li>{{ .Permalink }}</li>
    {{ end }}
    </ul>
{{ end }}

…and we can “include” it into other pages like this:

HTML
{{< gallery >}}

Here’s the commit with the changes - this will eventually become my “show image gallery for the current page” include.

This will output the following HTML:

HTML
<ul>
<li>http://localhost:1313/galleries/gallery1/img/Pope-Edouard-de-Beaumont-1844.jpg</li>
<li>http://localhost:1313/galleries/gallery1/img/Victor_Hugo-Hunchback.jpg</li>
<li>http://localhost:1313/galleries/gallery1/img/esmeralda.jpg</li>
<li>http://localhost:1313/galleries/gallery1/img/notebook.jpg</li>
</ul>

Auto-Generating Thumbnails

This is, besides the insane build speed, one of Hugo’s killer features for me: it’s able to batch-manipulate images at build time, which means that unlike my Jekyll image galleries, I just need to give Hugo the actual images, and it will auto-generate the thumbnails when building the site.

Here’s a simple example where I create a thumbnail for each image in the page bundle (150x115 pixels, JPEG quality 20%), and show it together with the original (larger) image:

/layouts/shortcodes/gallery.html:

HTML
{{ $image := (.Page.Resources.ByType "image") }}
{{ with $image }}
    <ul>
    {{ range . }}
    {{ $resized := .Fill "150x115 q20" }}
    <li>{{ .Permalink }}
        {{ $resized.Permalink }}</li>
    {{ end }}
    </ul>
{{ end }}

This will generate the following HTML:

HTML
<ul>
<li>http://localhost:1313/galleries/gallery1/img/Pope-Edouard-de-Beaumont-1844.jpg
    http://localhost:1313/galleries/gallery1/img/Pope-Edouard-de-Beaumont-1844_
    hu28e98c35df2fb9cf55bbe1469dad4e9d_67722_150x115_fill_q20_box_smart1.jpg</li>
<li>http://localhost:1313/galleries/gallery1/img/Victor_Hugo-Hunchback.jpg
    http://localhost:1313/galleries/gallery1/img/Victor_Hugo-Hunchback_
    hu0047bfedff79a029a47406b9671745f3_111947_150x115_fill_q20_box_smart1.jpg</li>
<li>http://localhost:1313/galleries/gallery1/img/esmeralda.jpg
    http://localhost:1313/galleries/gallery1/img/esmeralda_hueb36a26f61b343d8dba9f5eda0997ef5_
    54891_150x115_fill_q20_box_smart1.jpg</li>
<li>http://localhost:1313/galleries/gallery1/img/notebook.jpg
    http://localhost:1313/galleries/gallery1/img/notebook_hu3d03a01dcc18bc5be0e67db3d8d209a6_
    1586565_150x115_fill_q20_box_smart1.jpg</li>
</ul>

Note that in my real test project, I ignored the whole resources/_gen directory in source control. The Hugo docs tell me to commit the manipulated images, but I chose to ignore them because IMO they’re some kind of build artifact (like an executable, which I wouldn’t commit either).

For the local developer experience it doesn’t matter: on my local machine, Hugo only generates the images on the first build (because it doesn’t delete them before subsequent builds). And the deploy will happen via CI, so I don’t care if the CI server builds a few seconds longer.

However, in the demo project linked here, I did commit the generated images.

Putting It All Together

With all the “ingredients” prepared, we can now show the actual image gallery with Lightbox2:

/layouts/shortcodes/gallery.html:

HTML
<link rel="stylesheet" 
 href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.1/css/lightbox.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.1/js/lightbox.min.js">
</script>

{{ $image := (.Page.Resources.ByType "image") }}
{{ with $image }}
    {{ range . }}
    {{ $resized := .Fill "150x115 q20" }}
    <a href="{{ .Permalink }}" data-lightbox="x"><img src="{{ $resized.Permalink }}" /></a>
    {{ end }}
{{ end }}

HTML output:

HTML
<link rel="stylesheet" 
 href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.1/css/lightbox.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.1/js/lightbox.min.js">
</script>

<a href="http://localhost:1313/galleries/gallery1/img/Pope-Edouard-de-Beaumont-1844.jpg" 
 data-lightbox="x">
<img src="http://localhost:1313/galleries/gallery1/img/
 Pope-Edouard-de-Beaumont-1844_hu28e98c35df2fb9cf55bbe1469dad4e9d_67722_150x115_fill_
 q20_box_smart1.jpg" /></a>

<a href="http://localhost:1313/galleries/gallery1/img/Victor_Hugo-Hunchback.jpg" 
data-lightbox="x">
<img src="http://localhost:1313/galleries/gallery1/img/Victor_Hugo-Hunchback_
hu0047bfedff79a029a47406b9671745f3_111947_150x115_fill_q20_box_smart1.jpg" /></a>

<a href="http://localhost:1313/galleries/gallery1/img/esmeralda.jpg" 
data-lightbox="x"><img src="http://localhost:1313/galleries/gallery1/img/
esmeralda_hueb36a26f61b343d8dba9f5eda0997ef5_54891_150x115_fill_q20_box_smart1.jpg" /></a>

<a href="http://localhost:1313/galleries/gallery1/img/notebook.jpg" 
data-lightbox="x"><img src="http://localhost:1313/galleries/gallery1/img/
notebook_hu3d03a01dcc18bc5be0e67db3d8d209a6_1586565_150x115_fill_q20_box_smart1.jpg" /></a>

This is it - this HTML will display the thumbnails, and clicking on them will open the actual images with Lightbox2.

Note: There is one additional commit with a CSS file in the demo repo - this is because of the Ananke theme. Without that tweak, Ananke’s default CSS would stretch all images to the page width and show each one in a new row…which doesn’t make any sense for thumbnails.

Conclusion

So far, I like what I’ve seen from Hugo. As I said before, I find the learning curve insanely steep compared to Jekyll (and the docs don’t explain the basic concepts enough, so my main problem was not knowing the exact terms for the things I was looking for).

But once I got past that stage, working with Hugo is very nice because it comes with a lot of stuff built-in, which I needed to do manually in Jekyll, like:

  • getting a list of images belonging to a specific page (although I think Jekyll has improved in the meantime - I wrote the “Jekyll image gallery” posts in 2014)
  • automatic index pages
  • and, of course, auto-generating thumbnails!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)