$
to a variable if you need a global scope.Loop through pages
Let’s start with something easy. The following example loops through every page in content ordered by date with the newest first.
<div class="my-12">
{{ range .Site.RegularPages }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ .RelPermalink }}
</a>
{{ end }}
</div>
The next example is a loop through all pages that are inside the section (folder) /blog
and if there are no articles inside the blog, it shows “No articles found!”.
<div class="my-12">
{{ range where .Site.RegularPages "Section" "blog" }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ else }}
<p>
No articles found!
</p>
{{ end }}
</div>
Build is sort methods
To change the order you can use the built-in methods before making it more complicated with the sort
method. To change the order you can use the .Reverse
method. The
built-in sorting methods
are helpful in most situations.
<div class="my-12">
{{ range (where .Site.RegularPages "Section" "blog").ByTitle }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
In some situations it is very useful to get all articles without the one you are reading right now ordered by date. So the following range shows all articles except the one you’re reading right now.
<div class="my-12">
{{ range where (where .Site.RegularPages "Section" "blog") "Permalink" "!=" .Page.Permalink }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
To make it even more useful you can add the first
method and show only the latest 3 articles.
<div class="my-12">
{{ range first 3 (where (where .Site.RegularPages "Section" "blog") "Permalink" "!=" .Page.Permalink) }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
Difference between .Site.RegularPages
and .Site.Pages
With .RegularPages
you get all pages that are real pages for the actual language and no sections or taxonomy. If you use .Pages
you get every page for the actual language and this includes sections and taxonomy.
The difference to .AllPages
is that you get every page but it doesn’t consider if the page is available in the actual language.
Pages with multiple categories or tags
There are some use cases where you might need articles from different taxonomies combined or separated. The first example shows all articles that are having both tags.
<div class="my-12">
{{ $articles := where (where .Site.RegularPages "Section" "blog") "Permalink" "!=" .Page.Permalink }}
{{ range $articles.ByParam "some_param_you_like" }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
Would work too.
<div class="my-12">
{{ $articles := where (where .Site.RegularPages "Section" "blog") "Permalink" "!=" .Page.Permalink }}
{{ range sort $articles ".Params.some_param_you_like" "asc" }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
The second example shows how to get the first 3 articles that contain two tags.
<div class="my-12">
{{ $articles := where (where .Site.RegularPages "Section" "blog") "Permalink" "!=" .Page.Permalink }}
{{ range first 3 (where $articles "Params.tag" "intersect" (slice "tag1" "tag2")) }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
Range through taxonomy
Looping through Hugo’s taxonomy can be confusing sometimes so here are two ways how to range through it and show articles and terms on the page.
<div class="my-12">
{{ range $index, $taxonomy := .Site.Taxonomies }}
<h2 class="text-2xl">
Taxonomy: {{ $index }}
</h2>
{{ range $key, $value := $taxonomy }}
<div class="mb-4">
<h3 class="text-xl">Term: {{ $key }}</h3>
{{ range $value.Pages }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
{{ end }}
{{ end }}
</div>
<div class="my-12">
{{ range $key, $value := .Site.Taxonomies.categories }}
<div class="mb-4">
<h3 class="text-xl">Term: {{ $key }}</h3>
{{ range $value.Pages }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
{{ end }}
</div>
The second example only loops through a specific taxonomy like tags or categories.
The last example loops through all articles with a specific term inside a taxonomy. There are two ways to get access to the pages - both are working fine but only one can be used in all situations.
<div class="my-12">
{{ $articles := slice }}
{{ range slice "tag1" "tag2"}}
{{ range index $.Site.Taxonomies.tags . }}
{{ $articles = $articles | append .Page }}
{{ end }}
{{ end }}
{{ range $articles }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
<div class="my-12">
{{ range $key, $value := .Site.Taxonomies.categories.tag }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ dateFormat "January 2006" .Date }}
</a>
{{ end }}
</div>
This is NOT working
<div class="my-12">
{{ range $key, $value := .Site.Taxonomies.categories.tag-name }}
{{ end }}
</div>
Use this approach instead.
<div class="my-12">
{{ range $key, $value := index .Site.Taxonomies.topics "tag-name" }}
{{ end }}
</div>
Range - other areas of application
With the range
method you can do a lot more things like looping through numbers like a foo loop or using is like a for each loop. You can even split a string and loop through every word.
<div class="my-12">
{{ range $number := (seq 10) }}
<p class="text-xl">
{{ $number }}
</p>
{{ end }}
</div>
<div class="my-12">
{{ range $number := (slice "one" "two" "three" "four" "five") }}
<p class="text-xl">
{{ $number }}
</p>
{{ end }}
</div>
<div class="my-12">
{{ range $number := split "Lorem ipsum dolor sit amet consectetur adipisicing elit. Id, alias." " " }}
<p class="text-xl">
{{ $number }}
</p>
{{ end }}
</div>
Range with continue - Different approaches
There are a lot of cases where it might be handy if you could use something like break
or continue
in Hugo. And this topic does have a long history in the Hugo community. It was implemented and after some issues, removed in 2018. If you are interested in the details:
Revert CL 66410 “add break, continue actions in ranges”
.
The solution is not the nicest but it works.
<div class="my-12">
{{ range .Site.RegularPages }}
{{ if eq .RelPermalink $.Page.RelPermalink }}
<!-- Do nothing -->
{{ else }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ .RelPermalink }}
</a>
{{ end }}
{{ end }}
</div>
<div class="my-12">
{{ range .Site.RegularPages }}
{{ if not (eq .RelPermalink $.Page.RelPermalink) }}
<a href="{{ .RelPermalink }}" class="block">
{{ .Title }} / {{ .RelPermalink }}
</a>
{{ else }}
<!-- Do nothing -->
{{ end }}
{{ end }}
</div>
If you have a better workaround please send me a DM @markusantonwolf - I would love to hear about it.