Display a simple two-dimensional TOC of all posts


I currently migrate a dokowiki to hugo and most of the work is done.
I organized it now with an own template and I put the content in this folder structure:

What I would like to archive now is getting a simple list of all titles like dokuwiki is doing it:

But I have my problems that I do not know how to iterate over the posts to get on first level the first hierarchy of posts (e.g. FreeBSD) and under it the second hierarchy (e.g. TMP FS).

I tried now to create: themes/themename/layouts/section/posts.html with content:

{{ define "main" }}
<h1>{{ .Title }}</h1>
        {{ .Content }}
        <!-- Ranges through content/posts/*.md -->
        {{/* range where .Pages "Section" "posts" */}}
        {{range .Site.RegularPages}}
            <pre>content: {{ .Page }}</pre>
            <a href="{{ .Permalink }}">{{ .Date.Format "2006-01-02" }} | {{ .Title }}</a>
        {{ end }}
{{ end }}

But this lists only the markdown files but I miss the information how deep I’m in the hierarchy.

Maybe someone can point me into a direction how I can create such a list?

Thanks a lot

You’ll need to “walk” the content structure. This is a decent starting point.


Thanks, I tried to understand the idea and I think it is a kind of recursive walk.
But I have the problem of how to get my pages so I can work recursively.

It seems that I can get the first level (Kind=section) with:

    {{- range $firstLevel := .Site.Sections }}
        <p>1: Section: Title: {{ .Title }}, isSection: {{ .IsSection }}, Kind: {{ .Kind }}, Type: {{ .Type }}, Link: {{ .Permalink }}, Prev: {{ if .PrevInSection }}{{ .PrevInSection.Permalink }}{{ end }}, Next: {{ if .NextInSection }}{{ .NextInSection.Permalink }}{{ end }}</p>
        <p>1.1 Next: {{ with .Next }}{{ .Permalink }}{{ end }}, Prev: {{ with .Prev }}{{ .Permalink }}{{ end }}</p>
        <p>1.2 Current: {{ .CurrentSection }}</p>
        <p>1.3 FirstSection: {{ .FirstSection }}</p>
        <p>1.4 Sections: {{ .Sections }}</p>
        <p>1.5 Pages: {{ .Pages }}</p>
        {{- range $secondLevel := $firstLevel.Pages }}
            <p>2: Section: Title: {{ .Title }}, isSection: {{ .IsSection }}, Kind: {{ .Kind }}, Type: {{ .Type }}, Link: {{ .Permalink }}</p>
        {{- end}}
    {{- end }}

But I’m not able to find the second level which would be e.g. the directory freebsd, it returns the following:

1: Section: Title: Posts, isSection: true, Kind: section, Type: posts, Link: http://localhost:1313/posts/, Prev: , Next:
1.1 Next: , Prev:
1.2 Current: Page(/posts)
1.3 FirstSection: Page(/posts)
1.4 Sections: Pages(0)
1.5 Pages: Pages(170)
2: Section: Title: Cloud backup with rsync, isSection: false, Kind: page, Type: posts, Link: http://localhost:1313/posts/freebsd/cloud_backup/
2: Section: Title: Acme.sh, isSection: false, Kind: page, Type: posts, Link: http://localhost:1313/posts/freebsd/acme/
2: Section: Title: Convert from Dokuwiki to Hugo, isSection: false, Kind: page, Type: posts, Link: http://localhost:1313/posts/hugo/convert_from_dokuwiki/
2: Section: Title: HAProxy, isSection: false, Kind: page, Type: posts, Link: http://localhost:1313/posts/freebsd/haproxy/
2: Section: Title: Poudriere, isSection: false, Kind: page, Type: posts, Link: http://localhost:1313/posts/freebsd/poudriere/
2: Section: Title: Install FreeBSD, isSection: false, Kind: page, Type: posts, Link: http://localhost:1313/posts/netcup/install_freebsd/
2: Section: Title: Remote Reboot, isSection: false, Kind: page, Type: posts, Link: http://localhost:1313/posts/windows/remote_reboot/
2: Section: Title: ZFS, isSection: false, Kind: page, Type: posts, Link: http://localhost:1313/posts/freebsd/zfs/
2: Section: Title: Update FreeBSD, isSection: false, Kind: page, Type: posts, Link: http://localhost:1313/posts/freebsd/upgrade/

I tried also to play with functions:

{{ if $secondLevel.IsDecendant $firstLevel }}

But this does not work, it seems that IsDecendant does not exist:

Could you please help me how I can find the certain pieces in hierarchy.
Hierarchy Level 1:

  • posts

Hierarchy Level 2:

  • freebsd
  • android

Hierarchy Level 3 in FreeBSD:

  • TMP FS
  • Ports

From the referenced topic:

For this to work as desired, every directory must have an _index.md file.

Thanks a lot, I had now to rearrange a lot of files and in principle I get the list created.
But I have some questions.

I have the problem that only the else clause is matching, no matter on which level I’m:

I would like distinguish if the .LinkTitle is on the first level or if it is already an entry that is nested in here:

I also do not understand what .Eq is doing in line:

The content is currently structured like this:


Check the number of ancestor pages. For example:

{{ if eq (.Ancestors | len) 1 }}
  <a class="first-level" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
{{ else }}
  <a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
{{ end }}

.Eq is a method on .Page to test for equality between pages. A contrived example:

layouts/_default/single.html (iterating over every page on every page)

{{ range site.RegularPages }}
  {{ if $.Eq . }}
    <a class="current-page" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
  {{ else  }}
    <a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
  {{ end }}
{{ end }}

Note that iterating over every page on every page is a Very Bad Idea. That would be 2.5 billion iterations on a 50,000 page site.

Unable to reproduce.

Thanks at first a lot for you help, I really appriciate it and try to give back what I learned so far.
The beginning with Hugo is really not that easy to understand stuff and I learned a lot the last days.

I will post bellow what I did, maybe it will help someone else, too.

But I have one final question.
Lets assume I have more then one kind of content like


And I use the following part to generate the overview:

{{ partial "menus/section.html" (dict "page" . "pages" site.Sections) }}

How can I restrict it to list only entries for content “wiki” and not for content “post”.
I had problems with this nesting.
The moment I move all folders from:




it stops completely from working. So I was not able to play with a where condition with where eq site.Sections .Kind "wiki".

Just for reference as I do not create a menu but more a frontpage or a big index I use now the following code:

<nav class="gap-8 columns-1 lg:columns-2 xl:columns-3">
    <ul class="space-y-8 relative list-none">
        {{- partial "section-menu/walk.html" . }}

{{- define "partials/section-menu/walk.html" }}
  {{ $pages := sort .pages "Title" }}
  {{- range $pages }}
      <li class="pt-1 break-inside-avoid">
        {{ if eq .Kind "section" }}
        <h1 class="flex items-start p-3 rounded-xl text-gray-700 bg-gray-200">{{ .Title }}<span class="ml-1 text-sm">({{.Pages | len }})</span></h1>
        {{ else }}
          <a href="{{ .RelPermalink }}" class="pt-4 text-green-700 hover:underline hover:text-orange-500">{{ .Title }}</a>
        {{ end }}
        {{- with .Pages }}
          <ul class="pl-10 space-y-1 list-disc">
            {{- partial "section-menu/walk.html" (dict "page" $.page "pages" (sort . "Title")) }}
        {{- end }}
  {{- end }}
{{- end }}
{{ $sections := slice (site.GetPage "wiki") }}
{{ partial "menus/section.html" (dict "page" . "pages" $sections ) }}