_index.md in nested sections

I feel like this question has been asked before, but I haven’t yet been able to find a suitable answer.

I have several nested sections within the content directory of my site that organize my posts hierarchically. As long as I don’t add an “_index.md” file to any of the nested sections, every section list page properly displays posts of the given section and any child/grandchild section as well. As soon as I add an “_index.md” file to a child section, they are no longer displayed in the parent sections list page.

I’m using {{ range .Pages }} to get my page data. Is there a way to range through the current section as well as every other sub-section and grab the pages contained there. I like having the ability of the “_index.md” file to add front matter to my sections.

1 Like

Hi @phos_co, I feel there may be something else going on here which may explain the behavior you’re seeing. Would you share your project code, or create a small reproducible example and share that.

I appreciate your response. Here is what my content directory looks like. Every section has an “_index.md” file to control front matter.

  • /content
    • publications
      • _index.md
      • articles
        • _index.md
        • article #1
        • article #2
      • resources
        • _index.md
        • quotes
          • _index.md
          • quote #1
          • quote #2
        • compendium
          • _index.md
          • topic #1
          • topic #2

In my “layouts/publications” folder, I have a “list.html” file that contains the following:

{{ range .Pages }}
	{{ .Title }}
{{ end }}

Here is what is happening: Any section that does not contain a post directly under it, displays nothing. For example, because there are no posts directly under the “Publications” section, that pages does not show anything. If I navigate to /publications/articles, that list page will display the titles of “article #1” and “article #2”. The “Resources” page shows nothing, but /publications/resources/quotes shows the title for “quote #1” and “quote #2” etc.

What I’m wanting to accomplish is for “Publications” to show all posts, “Articles” to show all posts in and under the “articles” section, and so on. The “Resource” page to show all resources, quotes & compendium posts, etc. The above code does not work to accomplish that.

read again

When we talk about a section in correlation with template selection, it is currently always the root section only ( /blog/funny-cats/mypost/ => blog ).

If you need a specific template for a sub-section, you need to adjust either the type or layout in front matter.

For /resources and /publications you need a different list template!

{{ range .Sections }}
	{{ .Title }}
{{ end }}

Use the layout parameter in frontmatter to select this different template!

ex:
layout = “sectionlist”

and create template _default/sectionlist.html or layout/resources/sectionlist.html and layout/publications/sectionlist.html

1 Like

I appreciate your suggestion. Adding a layout parameter in the front matter is an excellent idea, and I will adopt that practice moving forward. Unfortunately, creating a new “sectionlist” template, and ranging through the “.Sections” variable only goes so far as displaying the titles of the sections one-step below the current list page. It does not display the posts that are included in those sub-sections, nor does it contain any grandchild-section titles or posts. I can run a {{ range .Pages }} from within the {{ range .Sections }}, but that seems to only pull current level posts, not posts from deeper within the hierarchical structure.

I was able to set up a sort of section tree navigation in order to accomplish this, but it wasn’t a simple solution, and so I’m unsure if it’s the best way to do it. I’m hoping there is an easier, more efficient way to accomplish this.

{{ if ne .IsHome true }}
	{{ template "current-section" . }}
{{ end }}

{{ if gt (len .Sections) 0}}
	{{ template "section-tree" . }}
{{end}}

<!-- Current Section -->
{{ define "current-section" }}
	{{ range .Pages }}
		{{ .Title }}
	{{ end }}
{{ end }}

<!-- Section Tree -->
{{ define "section-tree" }}
	{{ range .Sections }}
		{{ template "current-section" . }}
		{{ template "section-tree" . }}
	{{ end }}
{{ end }}

The reason I don’t like this solution is that the data pulled isn’t in one array, but over multiple instances of {{ range .Pages }}, so there is no practical use for functions like first and after.

** not tested **

You can use .Scratch to collect all pages in a map

{{ $scratch := newScratch }}
{{ range .Pages }}
       {{ $scratch.Add  .Title . }}
{{ end }}

and later range over it

1 Like

What @ju52 said ^

You can also do it via a slice

{{ $pages := slice }}

{{ range .Pages }}
  {{ $pages = $pages | append . }}
{{ end }}

<!-- some more ranges here where you keep appending to $pages -->

{{ range $pages }}
...
3 Likes

I came up with an interesting solution for this that I wanted to submit for critique - as I want to know if there are any downsides to my approach. However, the code is currently working exactly how I need.

Basically, I create a slice, as @zwbetz suggested, and set the page’s .CurrentSection in a variable. I then range through all the site’s regular pages that are within the page’s root .Section. I then filter through the returned pages to include only those that are within the $currenSection URL path. These remaining pages are then added to the slice.

{{ $pages := slice }}
{{ $currentSection := .CurrentSection }}

{{ range where .Site.RegularPages "Section" .Section }}
	{{ if in (.Permalink | string) $currentSection.RelPermalink }}
		{{ $pages = $pages | append . }}
	{{ end }}
{{ end }}

This has successfully allowed me to display all pages of the current section and every subsection with a minimal amount of code. When used within a layout template, as @ju52 suggested, it successfully allows me to have _index.md files for every section (in order to curate front matter), while still being able to display every page within a section’s heirarchy.

1 Like

To re-iterate what I was trying to accomplish, here is an example of my folder structure.

/content
	- section-1
		- _index.md
		- post-1.md
		- post-2.md

		-subsection-1.1
			- _index.md
			- post-3.md

		-subsection-1.2
			- _index.md
			- post-4.md

			-subsection.1.2.1
				- _index.md
				- post-5.md
				- post-6.md
				
/layouts
	- section-archive.html

My aim was to have every section list page be a sort of archive - displaying all the posts contained within the section and every relative subsection, while also employing an _index.md file to manage unique front matter for each section. Using the folder structure above, this is the behavior I was looking to accomplish:

If I were on the section-1 page, it would display every post from post-1.md to post-6.md. If I were then to navigate to subsection-1.2, that page would display post-4.md to post-6.md. subsection-1.2.1 would only display post-5.md and post-6.md, etc.

I did find a few different methods of accomplishing this, but some of them were limited. The reason I went with the code above is because it enabled to me to gather all of the pages from my site into a slice and then use specific functions like .ByDate to order the posts, or functions like first and after to filter the posts.

In employing this, I created a unique layout, such as section-archive.html and in the front matter of each section’s _index.md file, I set layout: "section-archive.html". This effectively tells the section page to use the section-archive.html template when rendering the pages.

The section-archive.html template then contains the following code:

{{ $pages := slice }}
{{ $currentSection := .CurrentSection }}

{{ range where .Site.RegularPages "Section" .Section }}
	{{ if in (.Permalink | string) $currentSection.RelPermalink }}
		{{ $pages = $pages | append . }}
	{{ end }}
{{ end }}

I hope this makes sense, and that it will be of help to someone looking to accomplish something similar.

2 Likes

@phos_co - this solution was exactly what I needed. The way you outlined the problem was very helpful and succinct. I have a content folder structure like:

  • content
    • products
      • brand1 (B1)
        • productLine1 (PL1)
          • B1 - PL1 - product1 .md
          • B1 - PL1 - product2
        • productLine2 (PL2)
          • B1 - PL2 - product1.md
          • B1 - PL2 - product2.md
        • _index.md
      • brand2 (B2)
        • productLine1 (PL1)
          • B2 - PL1 - product1.md
          • B2 - PL1 - product2.md
        • productLine2 (PL2)
          • B2 - PL2 - product1.md
          • B2 - PL2 - product2.md
        • _index.md
      • _index.md

This solution saved creating a layout templates at each stage of my folder tree. Thank you for sharing!

FWIW @ju52’s solution regarding the layout front matter approach worked as well. It just required that I be very pedantic in nesting my folder structures and assigning templates, so less than ideal.

To add to this for other beginners like me that might not spot the ability to range over the $page object immediately after generating. You can use:

{{ $currentSection := .CurrentSection }}

{{ range where .Site.RegularPages “Section” .Section }}
{{ if in (.Permalink | string) $currentSection.RelPermalink }}

         HTML CODE TO FOR LIST GOES HERE 

{{ end }}
{{ end }}

2 Likes

For folks coming across this thread, there is now a .RegularPagesRecursive page variable that I think will do what you need here. It was released with Hugo v0.68.0 in March, 2020.