Navigation Menus

Configure navigation menus in Bengal

6 min read 1134 words

Configure and customize navigation menus in Bengal.

Overview

Bengal supports multiple menu types that can be configured in frontmatter or configuration files.

Core vs. Theme Features

Bengal Core builds the menu data structure—this works with any theme. How menus are rendered (dropdowns, styling, hover behavior) depends on your theme. The default theme supports all features described here. Custom themes need to implement dropdown rendering themselves.

Bengal processes menu configuration in a clear hierarchy (most specific wins):

Priority Source Description Scope
1 [[menu.main]]in config Full manual control, disables all auto-magic Core
2 [menu.bundles.xxx]in config Define synthetic dropdowns (like Dev) Core
3 menu.dropdownin frontmatter Per-section dropdown configuration Core
4 Auto-discovery defaults Dev bundling, section discovery Core

Auto Topbar Menu

If you do not define[[menu.main]], Bengal generates a topbar menu automatically from your content sections.

  • Manual menu overrides auto menu: Ifmenu.mainis present and non-empty, Bengal uses it and does not auto-discover.
  • Auto menu only includes "real" sections: Auto-discovery builds items from sections that exist on disk.

Dev Dropdown (Default Behavior)

By default, Bengal bundles development-related links into a Dev dropdown:

  • GitHub: Ifparams.repo_urlis set
  • API Reference: If anapisection exists
  • CLI Reference: If aclisection exists

The Dev dropdown appears when 2 or more eligible items exist. With only one item, it appears as a standalone link.

Disable Auto Dev Bundle

[menu]
auto_dev_bundle = false

Customize the Dev Bundle

[menu.bundles.dev]
name = "Developer"     # Custom name (default: "Dev")
items = ["github", "api", "cli"]
min_items = 2          # Minimum items to show dropdown
weight = 90

Add one-off links to the auto-generated menu without replacing it entirely. Perfect for external links like forums, documentation, or social links:

# config/_default/menu.yaml (or bengal.toml)
menu:
  extra:
    - name: "Forum"
      url: "https://community.example.com/"
      weight: 100
    - name: "Discord"
      url: "https://discord.gg/example"
      weight: 101
      icon: chat-circle

Or in TOML:

[[menu.extra]]
name = "Forum"
url = "https://community.example.com/"
weight = 100

Extra items are appended to the auto-discovered menu and respect the same properties as manual menu items (name, url, weight, icon).

Section Dropdowns

Make any section display its children as a dropdown in the navigation.

Theme Support

Bengal Core builds menu items withchildren arrays. The default theme renders these as hover dropdowns. Custom themes can access the same data via item.childrenin templates.

Show Subsections

Addmenu.dropdown: true to a section's _index.md:

---
title: Documentation
menu:
  dropdown: true
---

This shows the section's subsections as dropdown children.

Data-Driven Dropdowns

Load dropdown items from a data file:

---
title: Learning Tracks
menu:
  dropdown: data:tracks
---

This loads items from data/tracks.yaml. Each key becomes a dropdown item linking to /{section}/{key}/.

Example: Tracks Data File

# data/tracks.yaml
zero-to-deployed:
  title: "Zero to Deployed"
  description: "Build your first site"

content-mastery:
  title: "Content Author Mastery"
  description: "Master advanced features"

Custom Bundles

Create synthetic dropdown menus that bundle multiple items:

[menu.bundles.resources]
name = "Resources"
url = "#"              # Non-clickable (dropdown only)
items = ["github", "api"]
min_items = 1
weight = 85

Available item types:

  • github - Links to params.repo_url
  • api - Links to /api/section
  • cli - Links to /cli/section

Frontmatter Configuration

Add pages to menus directly in frontmatter:

---
title: About Us
menu:
  main:
    weight: 10
  footer:
    weight: 5
    name: About  # Override display name
---

Configuration File Menus

Define menus inbengal.tomlfor non-content pages:

[[menu.main]]
name = "GitHub"
url = "https://github.com/your-org/your-repo"
weight = 100

[[menu.main]]
name = "Documentation"
url = "/docs/"
weight = 10
Property Type Description
name string Display text (defaults to page title)
url string Link destination
weight integer Sort order (lower = first)
icon string Icon name (e.g.,book, folder)
parent string Parent menu item for nesting
identifier string Unique ID for parent references

Nested Menus

Create hierarchical menus withparent and identifier:

# Parent item
---
title: Products
menu:
  main:
    identifier: products
    weight: 20
---

# Child item
---
title: Bengal Pro
menu:
  main:
    parent: products
    weight: 10
---

Non-Clickable Dropdown Parents

Default Theme Feature

The default theme automatically renders menu items withurl="#" and children as non-clickable dropdown triggers. Custom themes can check item.href == '#'to implement similar behavior.

When a menu item has children but no meaningful destination, useurl = "#"to make it non-clickable:

[[menu.main]]
name = "Resources"
url = "#"           # Not a link, just opens dropdown
identifier = "resources"
weight = 80

[[menu.main]]
name = "Blog"
url = "/blog/"
parent = "resources"

[[menu.main]]
name = "Newsletter"
url = "/newsletter/"
parent = "resources"

The parent "Resources" will open the dropdown on hover but won't navigate anywhere when clicked.

Tip

Bundles like Dev automatically useurl = "#"since they're just containers for their children.

Accessing Menus in Templates

Use theget_menu_lang()template function to retrieve menu items with proper localization:

{% let main_menu = get_menu_lang('main', current_lang()) %}
{% for item in main_menu %}
  <a href="{{ item.href | absolute_url }}">{{ item.name }}</a>
  {% if item.children %}
    <ul>
    {% for child in item.children %}
      <li><a href="{{ child.href | absolute_url }}">{{ child.name }}</a></li>
    {% end %}
    </ul>
  {% end %}
{% end %}

Note

Theget_menu_lang(name, lang) function returns menu items with active states already computed. Use item.href (not item.url) and apply | absolute_urlfor proper URL handling.

All themes have access to these properties on each menu item:

Property Type Description
item.name string Display name
item.href string Link URL (use with| absolute_urlfilter)
item.children list Child menu items (for dropdowns)
item.active bool True if current page
item.active_trail bool True if ancestor of current page
item.icon string Icon name (if set via frontmatter)
item.identifier string Unique ID for parent references

To render non-clickable dropdown triggers (like the default theme does):

{% if item.children and item.href == '#' %}
  {# Render as button/span, not link #}
  <span class="dropdown-trigger">{{ item.name }}</span>
{% else %}
  <a href="{{ item.href }}">{{ item.name }}</a>
{% end %}