Creating multi category pages in Hugo?


I have a query regarding Hugo and the generation of category pages. Generating individual category pages is straightforward, as shown below:

# this will generate a page /black with custom category slug
 - black

# this will generate a page /cat with custom category slug
 - cat

However, my question pertains to automatically generating category combination pages, such as “color + animal” resulting in /black-cat.

I’d like to understand how to implement this in Hugo and if there are any limitations on the number of categories that can be combined.

Your insights and assistance on this matter would be greatly appreciated. Thank you!


If this topic is challenging to discuss, or if there is already an existing solution that I haven’t yet discovered, it appears that either it isn’t of interest or it’s too complex to address.

Any comments or feedback would be greatly appreciated.

I don’t think it is possible to automatically create category combinations. What you could do is to create a section and in that section you define the URL and the combined categories via frontmatter.

For instance:

/combinations/ - the section (you will need layouts/combinations/single.html, and other layout files probably)

In /combinations/black-cat/ you have the following frontmatter:

- color: black
- animal: cat

Then in layouts/combinations/single.html you would use those frontmatter tags to range through a page collection with those two taxonomies set.

You will have to create one per combination, which might be a hassle.

Another solution might be to show all posts and implement some form of dynamic loading of posts via javascript that read options from the URL. like /combinations.html?color=black&animal=cat. For that you could create a custom post type that puts all your content files as JSON somewhere and then load dynamically.

Both ways need some to much work to create the solution.

1 Like

Hi @davidsneighbour

This is what i currently do but it is not manageable

# For example the totals i am working with are as follows
colors: 550 
Birds: 11,000+

i get a combination of 11,000 birds x 550 colors = 6,050,000 combinations. at this level it is almost imposible for me to remember which category combinations are already generated. But i think if hugo can do it automatically that is goign to way more usefull then the manual labour. And i think this will also extend the current use cases of hugo.

I am not sure if it is possible already in hugo as it will be stupid to generate manually all these pages if it currently possible in hugo.


That’s possible by using ExecuteAsTemplate or FromString, you’re able to create any custom pages or resources. But as you said, there will be a large number of the combinations/pages, I’m afriad it will increase the build time heavily, as this point, I’d prefer do it in JS that create an index JSON file, and fetch and filter the results by combination.

1 Like

Hi @razon

this is the second method i try, as i use js and load 12 cards from json and have the load more button. the problem is with the design while in hugo layout i have made a very complex card which automatically place logo watermark add ribbon etc.

But when using json i loose all this as the json fiel is being generated directly from the page front matter. if there was an easier version where i can generate the json file after the image and card processing such as using

{{ $resultsPerPage := 18 }} <!-- Set the number of results per page -->

{{ $allPages := where .Site.RegularPages "Section" "birds" }}

{{ $sortedPages := sort $allPages ".Params.title" "desc" }}

{{ $paginator := .Paginate $sortedPages $resultsPerPage }}

{{ $currentPageNumber := $paginator.PageNumber }}

{{ range $paginator.Pages }}

{{ partial "components/card.html" . }}

{{ end }}

So for example use the above layout and produce json data instead of html data.

IS this possible? I would like to get the card layout from the partial/components/card.html and get the remaining data from the front matter and generate the json file.


Yes, but I mean single filter page for all combinations, you can gather all neccessary data via template and put it in JSON (ExecuteAsTemplate or FromString, or custom output), and then using JS fetch the JSON and render the data on HTML page.

1 Like

Hi @razon

Here is the card layout i use

# this get image from the page front matter an image which is hosted in the assets directory
{{ $imagesrc := resources.Get .Params.coverImage }}

<!-- Image size 736 width-->
{{ $img736 := $imagesrc.Fill "736x552" }} 
{{ $imgwebp736 := $imagesrc.Fill "736x552 webp" }}

<!-- Image size 492 width-->
{{ $img492 := $imagesrc.Fill "492x369" }} 
{{ $imgwebp492 := $imagesrc.Fill "492x369 webp" }}

<!-- Text waterark for image size 736px-->  
{{ $x736 := sub $img736.Width 300 }}
{{ $y736 := sub $img736.Height 64 }}
{{ $copyrightText736 := slice  (images.Text "©copyright" (dict "color" "#ffffff" "size" 26 "linespacing" 2 "x" $x736 "y" $y736 )) }}
{{ $image736 := $img736.Filter $copyrightText736 }} 
{{ $imagewebp736 := $imgwebp736.Filter $copyrightText736 }}
<!-- Text waterark for image size 492px-->
{{ $x492 := sub $img492.Width 210 }}
{{ $y492 := sub $img492.Height 46 }}
{{ $copyrightText492 := slice  (images.Text "©copyright" (dict "color" "#ffffff" "size" 18 "linespacing" 2 "x" $x492 "y" $y492 )) }}
{{ $image492 := $img492.Filter $copyrightText492 }} 
{{ $imagewebp492 := $imgwebp492.Filter $copyrightText492 }}

        <!-- Image size untill 540px -->
        <source srcset="{{ $imagewebp492.RelPermalink | absURL }}" type="image/webp"
        media="(max-width: 540px)" width="{{ $imagewebp492.Width }}" height="{{ $imagewebp492.Height }}" />
        <source srcset="{{ $image492.RelPermalink | absURL }}" type="{{ $image492.MediaType }}"
        media="(max-width: 540px)" width="{{ $imagewebp492.Width }}" height="{{ $imagewebp492.Height }}" />

        <!-- Image size 736 - from 541px until 767px -->
        <source srcset="{{ $imagewebp736.RelPermalink | absURL }}" type="image/webp"
          media="(min-width: 541px) and (max-width: 767px)" width="{{ $image736.Width }}" height="{{ $image736.Height }}" />
        <source srcset="{{ $image736.RelPermalink | absURL }}" type="{{ $image736.MediaType }}"
          media="(min-width: 541px) and (max-width: 767px)" width="{{ $image736.Width }}" height="{{ $image736.Height }}" />
        <!-- Image size  492 - from 768px and above-->
        <source srcset="{{ $imagewebp492.RelPermalink | absURL }}" type="image/webp"
        media="(min-width: 768px)" width="{{ $imagewebp492.Width }}" height="{{ $imagewebp492.Height }}" />
        <source srcset="{{ $image492.RelPermalink | absURL }}" type="{{ $image492.MediaType }}"
        media="(min-width: 768px)" width="{{ $imagewebp492.Width }}" height="{{ $imagewebp492.Height }}" />
        <!-- Fall back image -->
        <img class="object-cover aspect-[4/3]" src="{{ $image736.RelPermalink | absURL }}"
          alt="Cover image for the listing property - {{ .Title }}" loading="lazy" width="{{ $image736.Width }}"
          height="{{ $image736.Height }}" />

Now i already have the json template which generate the all the json data like this:


{{- $.Scratch.Add "data" slice -}}
{{- range where .Site.RegularPages "Section" "properties-for-sale" -}}
    {{- with .Params -}}
        {{- $.Scratch.Add "data" (dict "title" .title "visible" .visible "date" .date "lastmod" .lastmod "type" .type "subtype" .subtype "details" .details "location" .location "features" .features "coverImage" .coverImage) -}}
    {{- end -}}
{{- end -}}
{{- $.Scratch.Get "data" | jsonify -}}

how can i use the $image736 which is generated in the html layout and use it in the json instead of the current "coverImage" .coverImage which is basically getting url from the page front matter?

since the image in the .Params.coverImage is not processed and this image is not available and json simply produce not found errors.

Thanks for taking the time to check and help

It’s same as the card layout does, and append it into the data, the pseudocode as follows.

{{/* Process the images as the card layout did. */))
{{- $image736 := ... }}
{{- with .Params -}}
        {{- $.Scratch.Add "data" (dict "title" .title ... "image_url" $image736.Permalink) -}}
 {{- end -}}

You can add more additional data via the dict function, such as the image height and width, or even an array of images.

{{/* Process the images as the card layout did. */))
{{- $images := slice
  (dict "url" $image736.Permalink "height" $image736.Height "width" $image736.Width)
{{- $.Scratch.Add "data" (dict "title" .title ... "images"  $images) -}}
1 Like

thanks @razon

I currently have around 16,000 images with complex filters, overlaid agent images, contact details, and I’m resizing them. I’m also converting them into different formats like webp. The issue is that it takes approximately 18 minutes for Hugo to build the website.

I’m wondering if I can use the images that are already created in the card layout for JSON, even though I’m not concerned about the local build time. The problem is that Hugo’s build time is consuming a significant portion of my allocated build minutes on Netlify. :disappointed:

can i tell hugo that this image i also want to use in my json layout and then in the json layout simply add "coverImage" .$savedImageFromCardLayout

Thank you for your help.

I’m not sure if Hugo will cache the images with same filters, but you can create a partial function and use partialCached to achieve this.

// layouts/partials/functions/post-imgs.html
{{/* Process images here */}}
{{ $img1  := ... }}
{{ $img2  := ... }}
{{ return dict "img1"  $img1 "img2"  $img2 }}

And then invoke the function in your templates.

{{ $imgs := partialCached "functions/post-imgs" . . }}
{{ $imgs.img1.Permalink }}

You’ll need to pay attention on the context and cache variant.

1 Like