Keith misner h0 vxgz5ty xa unsplash

Hugo partial: Sharing and inheritance of Hugo page resources

Hugo’s Page Bundle model keeps content and its resources together, but it doesn’t help when multiple pages need the same image. The standard approach — duplicating the file in each bundle — works until it doesn’t: one update missed, one path wrong, and things break quietly.

This partial retrieves resources from a page bundle but walks up the content hierarchy if the resource isn’t in the current page’s bundle, stopping at site assets if nothing is found closer. A single shared image in a section’s images/ directory becomes accessible to every page in that section with the same call used to fetch a page-local resource.

Let’s consider the following example of a directory structure:

 1content:
 2  articles:
 3    _index.md
 4    my-article:
 5      index.md
 6      images:
 7        feature-image.jpg
 8    images:
 9      index.md
10      share-image.jpg
11assets:
12  images:
13    logo.png

If you now use this partial in the file content/articles/my-article/index.md, you can access the three images by their relative paths.

As the feature image is specific to the article, it exists in the page bundle of the article my-article. We can access it using the following code:

1{{ $featureImage := partial "claris/_functions/resources/get" (dict 
2        "Page" .
3        "resource" "images/feature-image"
4) }}

Now the share image might also be specific to the article, but in my case, I use the same share image for all articles. The share image hence only exists in the branch bundle of the articles section articles.

Note

Ensure to turn images directory into a headless bundle: Add a file named index.md with the following content into every images directory that is part of a branch bundle (see below for an explanation what a branch bundle is)

1---
2headless: true
3---

If we have properly set up the images directory under articles as a headless bundle, we can access the share image share-image.jpg from the page bundle my-article using the following code:

1{{ $shareImage := partial "claris/_functions/resources/get" (dict 
2        "Page" .
3        "resource" "images/share-image"
4) }}

Perhaps surprisingly, the call to the resources partial is almost the same, even though we will get an image from a headless bundle one level up.

Finally, the site’s logo image will be the same for all articles. Hence, it makes sense to either put it into a headless bundle at the top of the content directory, or into the assets directory.

To access the share image, we use the following invocation of the resources partial:

1{{ $logoImage := partial "claris/_functions/resources/get" (dict 
2        "Page" .
3        "resource" "images/logo"
4) }}

Again, the call to the resources partial is almost the same, even though we will get an image from the global assets directory.

This partial accepts a dictionary with two key-value pairs: Page refers to the current page, while $resourcePath is the path of the resource. The path is treated as the prefix of the resource to be found, unless it ends with a file extension.

1{{ $resourcePath := "images/feature-image" }}
2{{ $resourceArgs := (dict
3"Page" .
4"resource" $resourcePath
5) }}
6
7{{- $image := partial "claris/_functions/resources/get" $resourceArgs }}

Hugo categorizes Page Bundles into Leaf Bundles and Branch Bundles. Leaf Bundles are directories containing an index.md file and can include a variety of content and attachments for single pages. Branch Bundles, represented by _index.md, need to be used for sections and typically only contain non-page resources (source: Hugo Page Bundles).

For Branch Bundles, the partial employs a workaround using Headless Bundles. By adding headless: true to the front matter of an index.md file within a directory, the directory can host resources similarly to a Leaf Bundle (detailed at Hugo Headless Bundles).

The partial uses the current page and the resource path as parameters, returning a Hugo resource object or false if not found.

Preference is given to page resources, with a fallback to site resources if the desired resource isn’t found in the current page or its ancestors (source: Hugo Page Resources).

A $debug variable can be set to true for insights into the operational flow, aiding in troubleshooting and enhancements.

This partial is part of my Hugo module claris-resources and hosted on GitHub. To use it in your Hugo website, add the following to your config/_default/hugo.yaml (or hugo.toml) file:

1module:
2  imports:
3    - path: github.com/simonheimlicher/claris-resources

This partial grew out of my own site’s needs. [Personal information, requires JavaScript] — I’m happy to extend it.