{"body_class":"rp-body rp-section-post","section":"post","thisUrl":"https://regisphilibert.com/blog/2018/01/hugo-page-resources-and-how-to-use-them/","title":"Hugo Page Resources | Regis Philibert","translation_url":null}

Hugo Page Resources

and how to use them...

Hugo 0.32 launched just before the new year and it brought along two massive improvements. Page Resources and Image Processing. Then Hugo 0.33 followed closely with a metadata management for the Page Resources

In this article we’ll cover Page Resources and its impact on the way we structure our content folders, what methods and properties it offers, how to use it in our templates and markdown and finally its newly metadata logic!

What are Page Resources?

Pages can now have their own images, .md files or any files stored in their own content folder or Bundle. You can then use those files in your templates with a special method called: .Resources.

How to Manage them?

Before Page Resources

  1. You had to store the files you would need for one page or post in the static folder of either your theme or your hugo install.
  2. Then you had to call for this image relative url in your markdown.
  3. You had to reference them in your Front Matter so your templates knew where to look.

I personnaly used to store them under a directory named after their respective Type so I could dynamically build a path in my templates.

β”œβ”€β”€ content
β”‚   β”œβ”€β”€ post
β”‚   β”‚   β”œβ”€β”€ i-love-eating-cupcakes.md
β”‚   β”‚   └── i-hate-baking-cupcakes.md
β”‚   └── page
└── static
    └── post
        β”œβ”€β”€ yummpy-cupcake.jpg
        β”œβ”€β”€ shiny-glaze.jpg
        β”œβ”€β”€ overcooked-dough.jpg
        └── sloppy-icing.jpg

After Page Resources: Hello Page Bundles

Now since Hugo 0.32, with Resources, you have a better option.

The content folder is a bit more clustered but every images/files are stored within their post directory Bundle. And their url will follow the posts’ too.

└── content
    β”œβ”€β”€ post
    β”‚   β”œβ”€β”€ i-love-eating-cupcakes
    β”‚   β”‚   β”œβ”€β”€ index.md // That's your post content and Front Matter
    β”‚   β”‚   └── images
    β”‚   β”‚       β”œβ”€β”€ yummy-cupcake.jpg
    β”‚   β”‚       └── shiny-glaze.jpg
    β”‚   └── i-hate-baking-cupcakes
    β”‚       β”œβ”€β”€ index.md // That's your post content and Front Matter
    β”‚       └── images
    β”‚           β”œβ”€β”€ overcooked-dough.jpg
    β”‚           └── sloppy-icing.jpg
    └── recipes

So to turn a page into a Page Bundle, you just make it a directory and an index.md at its root. Every other files in there will be considered its Resources.

You need to add Resources to a page of Kind section or taxonomy? Go on and head down there

What are their methods and properties?

Available methods for .Resources

.Resources.ByType (func)

Allow to retrieve all the page resources by type.

{{ with .Resources.ByType "image" }}
    <div class="Image">
    {{ range . }}
        <img src="{{ .RelPermalink }}">
    {{ end }}
{{ end }}

.Resources.Match (func)

Introduced in 0.34 .Match allows you to retrieve resources by Glob matching
Along the lines of our example above, it would go this way.

{{ with .Resources.Match "images/carousel/*" }}
    <div class="Carousel__slide">
    {{ range . }}
        <img src="{{ .RelPermalink }}">
    {{ end }}
{{ end }}
Glob matching?

To illustrate Glob let’s say you are looking for that sweet letter you got last year. Let’s see which glob would find this letter for you:

// Looking for letters/DearJohn.doc ?
.Resources.Match "letters/Dear*" βœ… 
.Resources.Match "letters/*.doc" βœ…
.Resources.Match "**.doc" βœ… 
.Resources.Match "**/dearjohn.doc" 🚫
.Resources.Match "*.doc" 🚫

.Resources.GetMatch (func)

Same as above but will only return the first matching resource.

ByPrefix, GetByPrefix (func)

Those 2 are deprecated (yep Hugo moves fast). But if you really still have to use them. They find resources using another form of matching, understand the begining of your filename.

Available properties for one resource.

What to do when I found it?

.ResourceType (string)

The type of the resource.

Now, it is based on MIME type, but will only hand out the main type. So when MIME type will give you application/pdf, .ResourceType will only give you application. See the full list of existing MIME type here.

Then how do I make the difference between a ZIP and a PDF, both sharing the main type application? We’ll cover this further down.

.Name (string)

By default this is the base filename (including the extension).
It can be overriden with the resource’s Front Matter metadata.

.Title (string)

Same default as .Name except for Resource of type page you can expect it to return their .Title.
For resources other than pages, it can be overriden with Resources Metadada covered below.

The absolute URL of the resource.

The relative URL of the resource.

How to use them?

Now. How can it benefit my coding?

In my templates?

Well from your template you can now easily retrieve the resources bundled with a post to say create a gallery in your single.html:

{{ with .Resources.ByType "image" }}
    <div class="Gallery">
        {{ range . }}
            <div class='Gallery__item'>
                <img src="{{ .RelPermalink }}" />
        {{ end }}
{{ end }}

Now all you will have to do is drop your images in the post bundle directory and voilΓ !

Metadata offer a simpler way, which we will cover later.

In my markdown?

You can also create a shortcode which will retrieve a particuliar resource by its matching filename.

{{ $img := $.Page.Resources.GetMatch (.Get 0)}}
    <img src="{{ $img.RelPermalink }}" alt="(.Get 1)" />
    <figcaption>(.Get 1)</figcaption>

And in your markdown:

{{< img "*overcooked-dough*" "Those cupcakes are way overcooked!" >}}

This is a pretty simple shortcode we’ll get deeper into the possibilites in another article.

Any kind of files

You can use any file type, but knowing what you’re dealing with is important. Trying to retrieve .Width of a PDF will result in an error.

Because .ResourceType will only give you the main type of your file, if you need to know if this resource of type application is a ZIP or a PDF file, for now, you’ll need to use strings.Contains on .RelPermalink as illustrated below1.

{{ with .Resources.ByType "application" }}
    {{ range . }}
            <a href="{{ .RelPermalink }}">
                {{ if (strings.Contains .RelPermalink ".pdf") }}
                    Check out this PDF!
                {{ else if (or (strings.Contains .RelPermalink ".doc") (strings.Contains .RelPermalink ".docx")) }}
                    Check out this Word Document!
                {{ else }}
                    Check out this... Thing!
                {{ end }}
    {{ end }}
{{ end }}

Or you could just use Page Resources Metadata which leads us to…

Page Resources Metadata

@bep has worked hard so we can manage metadata for our resources2.

Since Hugo 0.33, you can assign metadata directly from the bundle’s index.md Front Matter.
You will add an array called resources

This is how it looks like in the front matter. Those * in the src param look familiar? More below.

- src: "*/yummy-cupcake.jpg"
  title: "Yummy Cupcake"
  name: cupcake-1
- src: "*/shiny-cupcake.jpg"
  title: "Shiny Glaze"
- src: "*/*.jpg"
    credits: Myself the cook!

Target one or multiple files using src

Each item is targeted by its src parameter.

The src parameter will use the name of the file relative to the bundle to target which resource this metadata belongs to. Along the .Match Globbing mechanism, you can use the * to build that src.

Trying to match images/yummy-cupcakes.jpg :

src: 'images/yummy-cupcake.jpg' βœ…
src: '*/yummy-cupcake.jpg'      βœ…
src: 'images/*.jpg'             βœ…
src: 'images/*-cupcake.jpg'     βœ…
src: '*.jpg'                    βœ…

src: 'yummy-cupcake.jpg'        🚫
src: 'yummy-cupcake'            🚫

From our Front Matter exemple above we can see that yummy-cupcake.jpg and shiny-cupcake.jpg are getting respective titles.
On the other hand, every jpg images in the bundle including our two cupcakes will have a custom param credits

Metadata parameters

src is not really a parameter, you got that.


Name is the name of your file. This doesn’t seem much but it is very important.

I write it in lowercase, for this is the way in you Front Matter but you should really see it as .Name.

We already reviewed its default, now you can overwrite it.

Now remember those functions .Match and .ByMatch? This is what they use, the .Name. So once we apply a custom name to yummy-cupcake.jpg, you will have to .Match this new name and not the filename. In our case, trying to match yummy-cupcake.jpg:

{{ .Resources.Match "*/yummy-*" }} 🚫
{{ .Resources.Match "*/cupcake-*" }} βœ…

Using Front Matter resources metadata we changed the name of yummy-cupcake, it is now called cupcake-1 and will only respond when called by its new name even by .Match.

This may seem weird but it is actually very useful. If your image filename end up being some very complicated hash, you may simplify their targeting by specifying a name in the front matter and use .Match on that.


More like .Title, it holds the same default value as name, now you can overwrite it. This time, it won’t break anything.


This is an object for you to store anything you want, like for Page params, you retrieve them like so:

{{ range .Resources.Match "**.jpg" }}
<figure class="Figure">
    <img src="{{ .Permalink }}" alt="{{ .Title }}">
    {{ with .Params.credits }}
    <div class="Credits">
        {{ . }}
    {{ end }}

More on metadata.

Obviously there are plenty use cases for them. Already I can think of adding a draft param to some files I want to exclude from a particular range.

{{ if not (.Params.draft) }} // Check that the Resource is not marked as draft

Another use case is the building of a manifest shortcode to list certain files from your bundle right in your content.

What about Bundles for sections?

You can turn pages of any Kind into a Bundle so it benefits from Page Resources: sections, taxonomy even the home page.

But those take an _index.md instead of index.md.

If you inadvertently drop an underscoreless index.md in there, Hugo will mistake it for a single page’s Bundle.

Hugo terminology speaks of a Branch Bundle, as oppose the single page or Regular Page’s Leaf Bundle we’ve been covering so far.

Let’s go back to our structure example and focus on a Bundle for a section containing recipes.

β”œβ”€β”€ post
└── recipes
    β”œβ”€β”€ _index.md // That's your Section markdown and Front Matter 
    β”œβ”€β”€ recipes_header.jpg    // This is a resource of the section's Bundle.
    β”œβ”€β”€ all_recipes_print.pdf // Same as above.
    β”œβ”€β”€ chocolate-cupcakes.md // This a Regular Page inside the section.
    └── vanilla-cupcakes      // This is a Regular Page with a Bundle inside the section.
        β”œβ”€β”€ index.md
        β”œβ”€β”€ vanilla_cupcakes_print.pdf
        └── header.jpg
Want to learn more about Leaf Bundle vs. Branch Bundle in Hugo? You should start by the doc and keep on reeding this thorough piece by @kaushalmodi

Pratcice: Page manifest using Resources and Metadata

Let’s try and put in action what we learned. We’ll build a page that lists important files to download and print. Each file will of course be Resources from the page’s Bundle.

First we create a page.

title: "Bogus Application"
date: 2018-01-10

Hey there! Please fill the files below and send them back to us.



After adding the files, our page bundle structure should look like this

β”œβ”€β”€ index.md
└── documents (grouping them in a dir is optional)
    β”œβ”€β”€ guide.pdf
    β”œβ”€β”€ checklist.pdf
    β”œβ”€β”€ photo_specs.pdf
    └── payment.docx

Then we add some resources metadata to the Front Matter

title: "Bogus Application"
date: 2018-01-10T10:36:47-05:00

- src: 'documents/guide.pdf'
  name: Instruction Guide
    ref: '90564568'
- src: 'documents/checklist.pdf'
  name: Document Checklist
    ref: '90564572'
- src: photo_specs.pdf
  name: Photo Specifications
    ref: '90564687'
- src: 'documents/payment.docx'
  name: Proof of Payment
# Now our shared values
- src: '*.pdf'
    icon: pdf
- src: '*.docx'
    icon: word

Then we create a shortcode so we can add the file list anywhere in your content.

All its needs to do is to range on the resources stored in the document directory.

// shortcodes/manifest.html
    {{ range .Resources.Match "documents/*" }}
        <a target="_blank" href="{{ .Permalink }}">
            <i class="far fa-file-{{ .Params.icon }}"></i> {{ .Title }} <small>{{ with .Params.ref }}(Ref.{{ . }}) {{ end }}</small>
    {{ end }}

And voilΓ ! The result is here

We could improve the shortcode with some parameters, but for the sake of this article, we’ll keep it simple.

Now all you have to do is drop the shortcode in the page.

title: "Bogus Application"
date: 2018-01-10T10:36:47-05:00
draft: true

Hey there! Please fill the files below and send them back to us.

{{< manifest >}}



Page Resouces is still an early feature in the Hugo universe and is bound to improve.

Since I first published the article .Resources.Match and metadata have landed! Think of what’s coming ahead at this pace!

Use cases

There’s plenty of use cases to think about.

Of course a gallery comes to mind or a carousel, using the Front Matter metadata techniques for adding an image description or a carousel slide text and title.

Another use case could be to add rich full width sections to a page. Big title, big background images, critical CTA, all modern websites need those sectionned pages. Before Page Resources you had to use complicated attributes overstuffed “section” shortcodes in the single index.md file or worse too many ocean deep Front Matter objects. But now, all you have to do is drop some cleverly named .md files in your bundle with the minimum Front Matter and Markdown content. Then loop on them in your template, and voilΓ ! You build your sections!

Feel free to suggest improvements, use cases or your own discoveries in the comments!


  1. Special thanks to @bep for being patient with my pestering in the Hugo Discource and giving the strings.Contains and his amazing work on improving the already impressive Hugo.
  2. As of Hugo 0.34
comments powered by Disqus