How to create working Breadcrumbs for Hugo?

3.4k views Asked by At

It's unbelievable how difficult is to find solid information about slightly more advanced techniques with Hugo.

After quite a while searching I’ve found this article with a very nice way to implement breadcrumbs for Hugo.

I modified it a little bit to not add an unnecessary link to the last trail and to make use of i18n to translate the URL segments into something more human-readable. I also made a small workaround to remove some unwanted trails that doesn't match a translated entry:

{{- $url := replace .Permalink ( printf "%s" ( "/" | absLangURL ) ) "" -}}
{{- $url := replace .Permalink ( printf "%s" ( "/" | absLangURL ) ) "" -}}
{{- $.Scratch.Add "path" ( "/" | absLangURL ) -}}

{{- $.Scratch.Add "breadcrumb" (slice (dict "url" ( "/" | absLangURL ) "name" "home" "position" 1 )) -}}
{{- range $index, $element := split $url "/" -}}
  {{- $.Scratch.Add "path" $element -}}
  {{- $.Scratch.Add "path" "/" -}}
  {{- if ne $element "" -}}
    {{- $.Scratch.Add "breadcrumb" (slice (dict "url" ($.Scratch.Get "path") "name" . "position" (add $index 2))) -}}
  {{- end -}}
{{- end -}}

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [{{ range $.Scratch.Get "breadcrumb" }}{{ if ne .position 1 }},{{ end }}{
        "@type": "ListItem",
        "position": {{ .position }},
        "item": {
          "@id": "{{ .url }}",
          "name": "{{ .name }}"
        }
    }{{ end }}]
}
</script>

<nav class="breadcrumb">
  {{- $length := len ($.Scratch.Get "breadcrumb") -}}

  {{- range $index, $.Scratch.Get "breadcrumb" -}}

    <!--
    Assigning a constant as default value for i18n function
    if it doesn't match any entry
    Then check if current Breadcrumb item matches this constant,
    which means that part shouldn't be one of Breadcrumb's trail
    -->
    {{- $i18n := i18n ( print "breadcrumbs-" .name ) | default "__INTERNAL__" -}}

    {{- if not ( eq ($i18n) "__INTERNAL__" ) -}}

      {{ if eq ( (int .position) ) $length }}
  <span class="breadcrumb-item active">{{ $i18n }}</span>
      {{- else -}}
  <a class="breadcrumb-item" href="{{ .url }}" >{{ $i18n }}</a>
      {{- end -}}

    {{- end -}}

  {{ end }}
</nav>

It works very nicely except that as far as I could notice so far while navigating through Pagination links everything starts duplicating N times as far as the Pagination Page increases (i.e twice of Page 2, thrice for Page 3 and so on).

I've searched even more and found a similar implementation in which there's no duplication... but the links are all messed up, the URL of each trail is a concatenation of all links of all trails o.O

I try to avoid asking things on Hugo's "forum" because the answers given there are most of the times hard to understand, incomplete or with "pseudo-pseudo-codes".

Does anyone know how to make this work and could lend me a hand?

5

There are 5 answers

0
AudioBubble On BEST ANSWER

I've been forced to stop coding but since this question popped up a notification for me today, let me show what I did to solve the problem proposed in the question back there, even though I can't really explain why this happens -OR- if this still happens nowadays in newer versions of Hugo (I stopped using v0.28)

Well, after MUCH testing, I simply initialized a Template Variable with the scope of the “dot” that’s passed to the Partial Template when it's called and, in the Partial, instead of calling the Scratch from the context of the dollar-sign (or whatever it’s called in Hugo/Go) I used this variable I initialized:

list.html

{{ partial "breadcrumbs.html" . }}

partials/breadcrumbs.html

{{ $dot := . }}
{{ $dot.Scratch.Set "path" "" }}
{{ $dot.Scratch.Set "breadcrumb" slice }}

<!-- Rest of the code in the original question -->

And from them on, instead of, for example:

{{ $.Scratch.Add "path" $element }}

I used:

{{ $dot.Scratch.Add "path" $element }}

And all Pagination elements worked without duplicates.

2
Lars Blumberg On

It only takes 2 steps, inspired by: http://hugocodex.org/add-ons/breadcrumbs/

  1. Create a file named breadcrumbs.html in the folder layouts/partials of your Hugo project
<span>
    <a href="/">Home</a>
    {{ range (split .URL "/") }}
        {{ if gt (len . ) 0 }}
            / <a href="/{{ . }}">{{ humanize (replace . "posts" "blog") }}</a>
        {{ end }}
    {{ end }}
</span>
  1. Include breadcrumbs.html where you need it with:

{{ partial "breadcrumbs.html" . }}

2
Ken On

From the Hugo documentation and building on Lars's answer:

  1. Create a file named breadcrumbs.html in the folder layouts/partials of your Hugo project or theme
<ol  class="nav navbar-nav">
    {{ template "breadcrumbnav" (dict "p1" . "p2" .) }}
</ol>
{{ define "breadcrumbnav" }}
{{ if .p1.Parent }}
{{ template "breadcrumbnav" (dict "p1" .p1.Parent "p2" .p2 )  }}
{{ else if not .p1.IsHome }}
{{ template "breadcrumbnav" (dict "p1" .p1.Site.Home "p2" .p2 )  }}
{{ end }}
<li{{ if eq .p1 .p2 }} class="active"{{ end }}>
    <a href="{{ .p1.Permalink }}">{{ .p1.Title }}</a>
</li>
{{ end }}
  1. Include breadcrumbs.html where you need it with:
{{ partial "breadcrumbs.html" . }}

This produces links (including the sections) that are all valid.

0
Raunak Tripathi On

Well, this can easily be solved by replacing.URL to .RelPermalink from Lark Blumberg's solution earlier from this timeline.

Step 1:

 <div class="container" id="breadcrumbs">
     <a href="/">Home</a>
     {{ range (split .RelPermalink "/") }}
         {{ if gt (len . ) 0 }}
            <i class="fas fa-chevron-right"></i><a href="/{{ . }}">{{ humanize (replace . "posts" "blog") }}</a>
         {{ end }}
     {{ end }}
 </div>

Step 2: Include breadcrumbs.html where you need it with:

{{ partial "breadcrumbs.html" . }}

For more information goto Breadcrumbs | HugoCodex

0
Apoorv Mote On

I wrote blog post on adding breadcrumb partial for hugo with strucutred data.

Don't just show machine readable breadcrumb to search engine crawler but also show the same to user also. My blog post goes into detail on the topic.

To answer this question here is my partial for only crawlers.

{{ $url := replace .Permalink (printf "%s" .Site.BaseURL) ""}}
{{ .Scratch.Add "path" .Site.BaseURL }}
{{ .Scratch.Add "breadcrumb" (slice (dict "url" .Site.BaseURL "name" "Home"))}}
{{ .Scratch.Add "permalink" .Permalink }}
{{ .Scratch.Add "title" .Title }}
{{ $pScratch := .Scratch }}

{{ range $index, $element := split $url "/" }}
  {{ $pScratch.Add "path" $element }}
  {{ $pScratch.Add "path" "/" }}
  {{ if ne $element "" }}
    {{ if eq ($pScratch.Get "path") ($pScratch.Get "permalink") }}
        {{ $pScratch.Add "breadcrumb" (slice (dict "url" ($pScratch.Get "path") "name" ($pScratch.Get "title")))}}
    {{ else }}
        {{ $pScratch.Add "breadcrumb" (slice (dict "url" ($pScratch.Get "path") "name" (humanize .)))}}
    {{ end }}
  {{ end }}
{{ end }}

<script type="application/ld+json">
  [{
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "itemListElement": [
      {{ range $index, $breadcrumb := .Scratch.Get "breadcrumb" }}
        {{ if ne $index 0 }},{{ end }}
          {
            "@type": "ListItem",
            "position": {{ add $index 1 }},
            "name": "{{ $breadcrumb.name }}",
            {{ if ne $breadcrumb.url ($pScratch.Get "permalink") }}
              "item": {{ printf "%s" $breadcrumb.url }},
            {{ end }}
          }
      {{ end }}
      ]
    }
    {{ range $index, $category :=  .Params.categories }}
      ,
      {
        "@context": "https://schema.org",
        "@type": "BreadcrumbList",
        "itemListElement": [
          {
            "@type": "ListItem",
            "position": 1,
            "name": "Home",
            "item": "{{$.Site.BaseURL}}",
          },
          {
            "@type": "ListItem",
            "position": 2,
            "name": "Categories",
            "item": "{{$.Site.BaseURL}}categories/",
          },
          {
            "@type": "ListItem",
            "position": 3,
            "name": "{{ humanize . }}",
            "item": "{{$.Site.BaseURL}}categories/{{.}}",
          },
          {
            "@type": "ListItem",
            "position": 4,
            "name": "{{ $pScratch.Get "title" }}",
          },
        ]
      }
    {{ end }}
    ]
</script>