Multipage posts

Hi @jmooring. I got the setup you shared here to work. Thanks for that. However, I converted multipage = true to layout = "multipage" since I had already designed the layout and only incorporated the code in your single.html for posts.
A small issue occurs though when I include the list on homepage or a separate blog page. The list of pages appears with every post showing in the ranged list, with the paginated pages showing up as blank (see Screenshot by Lightshot). Is there a way to stop this from happening? Below is a sample of the code I am using to range through all posts

          {{- $p := where site.RegularPages "Type" "post" }}
          {{- $p1 := where (where site.Pages "Type" "post") "Layout" "multipage" }}
          {{- $pages := union $p1 $p2 -}}
          {{ range $pages.ByDate.Reverse | first 10 -}}
           <-- code goes here -->
          {{ end }}

Edit: I also added url: to the front matter of the index file and the other files, so the permalinks are consistent.

What is $p2?

Sorry. Typo. Just $p.

If I understand what you’ve done, this will not work because the layout value is cascaded to both regular and section (_index.md) pages.

You will have to modify your multipage layout to handle both page kinds.

layouts/_default/multipage.html
{{ define "main" }}

  {{ if .IsSection }}
    <h1>{{ .Title }}</h1>
  {{ else }}
    <h1>{{ .Title }} (continued)</h1>
  {{ end }}

  {{ .Content }}

  {{ $p := append .CurrentSection.Pages.ByWeight (slice .CurrentSection) }}
  {{ if .IsSection }}
    {{ with $p.Prev . }}
      <a href="{{ .RelPermalink }}">Next page</a>
    {{ end }}
  {{ else }}
    {{ with $p.Next . }}
      <a href="{{ .RelPermalink }}">Previous page</a>
    {{ end }}
    {{ with $p.Prev . }}
      <a href="{{ .RelPermalink }}">Next page</a>
    {{ end }}
  {{ end }}

{{ end }}

And for your home page…

{{ range (site.GetPage "/posts").Pages }}
  <h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
{{ end }}

You might have to change β€œposts” to β€œpost” depending on how you have named your sections.

I should mention that these pages are part of nested sections, so it is not a whole section on its own. So, each nested section will have these multipage posts in future. If I don’t cascade the layout, the navigation buttons disappear on page 2 and subsequent pages.

Here’s a modified example that uses the layout front matter value:

git clone --single-branch -b hugo-forum-topic-41042 https://github.com/jmooring/hugo-testing hugo-forum-topic-41042
cd hugo-forum-topic-41042
hugo server

Tweak as needed.

2 Likes

If your nested subsections look like this:

structure
content/
β”œβ”€β”€ de/
β”‚   β”œβ”€β”€ posts/
β”‚   β”‚   β”œβ”€β”€ post-1/
β”‚   β”‚   β”‚   β”œβ”€β”€ page/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ 2.md
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ 3.md
β”‚   β”‚   β”‚   β”‚   └── 4.md
β”‚   β”‚   β”‚   └── _index.md
β”‚   β”‚   β”œβ”€β”€ subsection-a/
β”‚   β”‚   β”‚   β”œβ”€β”€ post-3/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ page/
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ 2.md
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ 3.md
β”‚   β”‚   β”‚   β”‚   β”‚   └── 4.md
β”‚   β”‚   β”‚   β”‚   └── _index.md
β”‚   β”‚   β”‚   β”œβ”€β”€ _index.md
β”‚   β”‚   β”‚   └── post-4.md
β”‚   β”‚   β”œβ”€β”€ _index.md
β”‚   β”‚   └── post-2.md
β”‚   └── _index.md
└── en/
    β”œβ”€β”€ posts/
    β”‚   β”œβ”€β”€ post-1/
    β”‚   β”‚   β”œβ”€β”€ page/
    β”‚   β”‚   β”‚   β”œβ”€β”€ 2.md
    β”‚   β”‚   β”‚   β”œβ”€β”€ 3.md
    β”‚   β”‚   β”‚   └── 4.md
    β”‚   β”‚   └── _index.md
    β”‚   β”œβ”€β”€ subsection-a/
    β”‚   β”‚   β”œβ”€β”€ post-3/
    β”‚   β”‚   β”‚   β”œβ”€β”€ page/
    β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ 2.md
    β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ 3.md
    β”‚   β”‚   β”‚   β”‚   └── 4.md
    β”‚   β”‚   β”‚   └── _index.md
    β”‚   β”‚   β”œβ”€β”€ _index.md
    β”‚   β”‚   └── post-4.md
    β”‚   β”œβ”€β”€ _index.md
    β”‚   └── post-2.md
    └── _index.md

Then you can range like this on your home page:

{{ range (where site.Pages "Type" "posts").ByTitle }}
  {{ if or
    (and .IsPage (not (eq .Layout "multipage" )))
    (and .IsSection (eq .Layout "multipage" ))
  }}
    <h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
  {{ end }}
{{ end }}

I’ve updated the test site to include a subsection.

This is definitely what I have been looking for all this time! Thanks for the time and effort you’ve put into this Joe! Just a minor issue now relating to first that I will try to describe as I can. (I edited your code to sort by date and in reverse).

  1. Assume the multipage post has two pages and its own _index.md` file.
  2. Using your code above, adding first 5 to the range shows only the title of the multipage post. I assume the pages inside it are counted in the limit
  3. When I change the limit to first 6 the next post (or page as it is know in Hugo), which is a regular page (not multipage), shows up in the sequence.
  4. So, instead of having six posts, now there are only two posts appearing on the homepage/blog page.
  5. Is there a way to make sure this does not happen? (I want to limit by 10 so only ten posts show up, including the multipage post).

Build the collection, then limit with first.

{{ $p := slice }}
{{ range (where site.Pages "Type" "posts").ByTitle }}
  {{ if or
    (and .IsPage (not (eq .Layout "multipage" )))
    (and .IsSection (eq .Layout "multipage" ))
  }}
    {{ $p = $p | append . }}
  {{ end }}
{{ end }}

{{ range $p.ByTitle.Reverse | first 2 }}
  <h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
{{ end }}
1 Like

One last question. How would .Paginate work with this code? It works flawlessly on the homepage, but I cannot replicate it on my blog page with $paginator.

{{ $p := slice }}
{{ range (where site.Pages "Type" "posts").ByTitle }}
  {{ if or
    (and .IsPage (not (eq .Layout "multipage" )))
    (and .IsSection (eq .Layout "multipage" ))
  }}
    {{ $p = $p | append . }}
  {{ end }}
{{ end }}

{{ range (.Paginate ($p.ByDate.Reverse | first 10)).Pages }}
  <h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
{{ end }}
{{ template "_internal/pagination.html" . }}
3 Likes

Thanks for all this! I will have to redo some code here and there and edit existing posts to accommodate this solution (I had written a few multipage ones). Cheers!

1 Like

Another small issue detected regarding Content Sections | Hugo. When using .Parent, the paginated pages inherit their own folder’s _index.md instead of the nested section. Do you have an idea on how to get around this? So, for example, in the <head>, for related posts in the nested section, and on JSON-LD schema, I use .Parent e.g to add the meta for article section or to limit the posts to a nested section. When on a paginated multi-page, the title in the multipage post’s _index_md appears with .Parent set instead of the title in the nested section’s _index.md.

I don’t fully understand your question, but you can check upwards with something like:

{{ with .Parent }}
  {{ with .Parent }}
    {{ .Title }}
  {{ else }}
    {{ .Title }}
  {{ end }}
{{ end }}
3 Likes

This resolved two of the questions. The last one is concerning related pages. I am trying to adapt the code code you shared above to show related posts by nested section. For multipage posts, on the first page, posts from the same nested section as the multipage show up alright. But on the paginated pages, the related posts are the pages inside the multipage post with .Parent set. See my code below.

        {{- $p := slice -}}
        {{- range (where (where site.Pages "Type" "post") "Permalink" "ne" .Page.Permalink).ByDate -}}
        {{- if or
        (and .IsPage (not (eq .Layout "multipage" )))
        (and .IsSection (eq .Layout "multipage" ))
      -}}
        {{- $p = $p | append . -}}
        {{- end -}}
        {{- end -}}
       {{- $section := $p.ByDate.Reverse -}}
      {{- $section := .Parent -}}
      {{- $filteredPosts := $section.Pages -}}
      {{- $relatedPosts := shuffle $filteredPosts | first 5 -}}
      {{ range $relatedPosts }}
      <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
      {{- end }}
      {{- end }}

In which template are you trying to place this code?

It is a partial. So I put it in single and multipage layouts.

content/en/
β”œβ”€β”€ posts/
β”‚   β”œβ”€β”€ post-1/
β”‚   β”‚   β”œβ”€β”€ page/
β”‚   β”‚   β”‚   β”œβ”€β”€ 2.md
β”‚   β”‚   β”‚   β”œβ”€β”€ 3.md
β”‚   β”‚   β”‚   └── 4.md
β”‚   β”‚   └── _index.md
β”‚   β”œβ”€β”€ subsection-a/
β”‚   β”‚   β”œβ”€β”€ post-3/
β”‚   β”‚   β”‚   β”œβ”€β”€ page/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ 2.md
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ 3.md
β”‚   β”‚   β”‚   β”‚   └── 4.md
β”‚   β”‚   β”‚   └── _index.md
β”‚   β”‚   β”œβ”€β”€ _index.md
β”‚   β”‚   └── post-4.md
β”‚   β”œβ”€β”€ _index.md
β”‚   └── post-2.md
└── _index.md

When visiting /en/posts/post-2/, should the related pages be:

/en/posts/post-1/

Or

/en/posts/post-1/
/en/posts/subsection-a/

Neither. Let’s use the example of subsection-a. When visiting post-3 and its sub-pages, the related pages should be post-4 and any other page inside subsection-a. However, when pages 2-4 are open, then the related pages are the pages themselves inside post-3, rather than post-4 and any other pages in that subsection.

You didn’t answer my question.

When visiting /en/posts/post-2/, what should the related pages include?

Pull changes on the test site and see if that’s what you want.