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
- 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.
- Then you had to call for this image relative url in your markdown.
- 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.
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 }}
</div>
{{ 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 }}
</div>
{{ 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 prose stored as 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 / .MediaType
You have serveral option to retrieve the MIME type of the resource, but it also comes with a few methods to pinpoint what you really need1.
If our resource was a PDF this is what you could get using .MediaType
.ResourceType
πpdf
.MediaType
πapplication/pdf
.MediaType.MainType
πapplication
.MediaType.SubType
πpdf
.MediaType.Suffixes
π[pdf]
(a slice of suffixes)
.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.
.Permalink (string)
The absolute URL of the resource.
.RelPermalink (string)
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 }}" />
</div>
{{ end }}
</div>
{{ 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.
# shortcodes/img.html
{{ $img := $.Page.Resources.GetMatch (.Get 0)}}
<figure>
<img src="{{ $img.RelPermalink }}" alt="(.Get 1)" />
<figcaption>(.Get 1)</figcaption>
</figure>
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.
Don’t be fooled .MediaType.SubType
it does not hold a file extension.
For exemple, a docx
file will return a .SubType
of vnd.openxmlformats-officedocument.wordprocessingml.document
making it impossible to safely test it.
Depending on why you are testing the file type for, I find the most simple and safest way to tell what is what is to use in
or intersect
in conjunction with .MediaType.Suffixes
as illustraded below:
{{ with .Resources.ByType "application" }}
<ul>
{{ range . }}
<li>
<a href="{{ .RelPermalink }}">
{{ if (in .MediaType.Suffixes "pdf") }}
Check out this PDF!
{{ else if (intersect .MediaType.Suffixes (slice "docx" "docm")) }}
Check out this Wordish Document!
{{ else }}
Check out this... Thing!
{{ end }}
</a>
</li>
{{ end }}
</ul>
{{ end }}
- For PDFs, we only have one possible extension, so using
in
, we just check if thepdf
string is in the slice returned by.MediaType.Suffixes
. - Word documents can either be
docx
ordocm
so we useintersect
to check if any of the two is included in the suffixes slice.
Page Resources Metadata
@bep has worked hard so we can manage metadata for our resources23.
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.
resources:
- src: "*/yummy-cupcake.jpg"
title: "Yummy Cupcake"
name: cupcake-1
- src: "*/shiny-cupcake.jpg"
title: "Shiny Glaze"
- src: "*/*.jpg"
params:
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
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 .GetMatch
? 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.
title
More like .Title
, it holds the same default value as name, now you can overwrite it. This time, it won’t break anything.
params
This is an object for you to store anything you want, like for Page params, you retrieve them like so:
{{ range $.Page.Resources.Match "**.jpg" }}
<figure class="Figure">
<img src="{{ .Permalink }}" alt="{{ .Title }}">
{{ with .Params.credits }}
<div class="Credits">
{{ . }}
</div>
{{ end }}
</figure>
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
Practice: 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.
::FILE LIST WILL GO HERE::
Thanks!
After adding the files, our page bundle structure should look like this
bogus-application
βββ 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
resources:
- src: 'documents/guide.pdf'
name: Instruction Guide
params:
ref: '90564568'
- src: 'documents/checklist.pdf'
name: Document Checklist
params:
ref: '90564572'
- src: photo_specs.pdf
name: Photo Specifications
params:
ref: '90564687'
- src: 'documents/payment.docx'
name: Proof of Payment
# Now our shared values
- src: '*.pdf'
params:
icon: pdf
- src: '*.docx'
params:
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
<ul>
{{ range .Resources.Match "documents/*" }}
<li>
<a target="_blank" href="{{ .Permalink }}">
<i class="far fa-file-{{ .Params.icon }}"></i> {{ .Title }} <small>{{ with .Params.ref }}(Ref.{{ . }}) {{ end }}</small>
</a>
</li>
{{ end }}
</ul>
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 >}}
Thanks!
Conclusion
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!