Another way to make a menu item active

I ran into a scenario recently where my menu item would be active when viewing my blog posts list page, but not when viewing an individual blog post.

As an example, let’s say my config.toml menu looks like:

[menu]
  [[menu.nav]]
    name = "Home"
    url = "/"
    weight = 1
  [[menu.nav]]
    name = "Blog"
    url = "/post/"
    weight = 2

The “Blog” menu item is active when viewing the list page URL:

/post/

But not when viewing a single page URL:

/post/some-blog-post/

I solved it checking if the current menu iteration name was “Blog” and if the current section was “post”. I did the same thing for my “Tags” pages. Here’s a stripped down version of the solution:

{{ $current := . }}
{{ range .Site.Menus.nav }}
  {{ $active := or ($current.IsMenuCurrent "nav" .) ($current.HasMenuCurrent "nav" .) }}
  {{ $active = or $active (eq .Name $current.Title) }}
  {{ $active = or $active (and (eq .Name "Blog") (eq $current.Section "post")) }}
  {{ $active = or $active (and (eq .Name "Tags") (eq $current.Section "tags")) }}
  <a href="{{ .URL }}" class="{{ if $active }}active{{ end }}">{{ .Name }}</a>
{{ end }}

Here’s the full nav partial and the full menu config. You can see it in action here by clicking any of the blog posts and noticing the “Blog” menu is still set to active.

If others know of a better way to solve this, please do share.

13 Likes

great tip, thanks man. I’m using this for my blog!

1 Like

Super elegant way of adding up or clause without impeding readability!
I’ll use this syntax a lot and not only for menu items :slight_smile:

2 Likes

This is one of those examples that is worth adding to the docs, IMHO. Thanks much for sharing!

2 Likes

Would it be possible to explain the logic of the code for those of us who don’t read it like a book?

Also, I am not sure what exactly to look for. What makes the menu look active?

Sorry, still learning.

I’m on my phone so this will be terse.

Mark the menu item as active if the menu item name equals current page title:

{{ $active = or $active (eq .Name $current.Title) }}

Mark the menu item as active if the current menu item name is Blog and the current page section is post:

{{ $active = or $active (and (eq .Name "Blog") (eq $current.Section "post")) }}

Mark the menu item as active if the current menu item name is Tags and the current page section is tags:

{{ $active = or $active (and (eq .Name "Tags") (eq $current.Section "tags")) }}

In the case of the Cupper theme, the menu item will be visually highlighted.

2 Likes

Note that were you menus config added through individual page’s Front Matter, you could access any Menu item’s page with .Page, and therefor perform more faithful and generic tests than with .Name.

Per example instead of testing the menu item’s .Name against the name of current page’s section, you could compare the sections.

  {{ $active := or ($current.IsMenuCurrent "nav" .) ($current.HasMenuCurrent "nav" .) }}
  {{ $active = or $active (eq .Page $current) }}
  {{ $active = or $active (eq .Page.Section $current.Section)) }}
3 Likes

In my setup, I used comparison of the urls to achieve this. No need for any or clause:

{{ $active := in $current.RelPermalink .URL }}
1 Like

@kascme I like this way too. But I use the filename for my permalinks so this wouldn’t work in my specific case

True. But if you use path.Dir you should get the menu link marked as active as well:

{{ $active := in $current.RelPermalink (path.Dir .URL) }}
1 Like

Unfortunately that doesn’t work as expected. This is a screenshot of the menu while on the “Blog” page (only the Blog menu item should be highlighted).

For debugging, I’ve printed the path.Dir for each menu item.

Playing off your syntax, I got the below snippet to work as expected, but I prefer my original way.

{{ $active := or ($current.IsMenuCurrent "nav" .) ($current.HasMenuCurrent "nav" .) }}
{{ $active = or $active (eq .Name $current.Title) }}
{{ $active = or $active (and (eq (trim (path.Dir .URL) "/") $current.Section) (ne $current.Section "")) }}
1 Like

@ zwbetz: You are right. I had no Home nor any other root link in my menu, so it was working for me. I guess a check if the menu item belongs to Kind section or page would also work. But that’s again adding extra lines. My example above made using .IsMenuCurrent, .HasMenuCurrent obsolete, so it was just a one liner. That’s why I suggested it, but as you pointed out, it is not working for every use case.

thank you everyone for this.
.IsMenuCurrent/.HasMenuCurrent seem bugged…

A post was split to a new topic: Mark child menu items as active

Does it work for a multilingual menu? Shouldn’t it be compared with .relLangUrl?

For multilingual menus you can use this:

{{ range . }}
  {{ $active := eq $.RelPermalink (.URL | relLangURL) }}
  {{ $active = or $active (eq $.Section (lower .Name)) }}
  <li>
    <a href="{{ .URL | relLangURL }}"{{ if $active }} class="active"{{ end }}>{{ .Name }}</a>
  </li>
{{ end }}
1 Like

I don’t understand what’s going on ?

http://localhost:1313/en/what-we-do/

This looks correct. .URL | relLangURL and .RelPermalink are identical. Do they both change when you click on another link? (You posted a link to localhost.)

Thank you for your time.
So far I haven’t deployed anything. Localhost was for context.
Indeed when I click on the other entries nothing changes.