How to use the script
- Save the script: Save the code above as 
generate_menu.pyin the root directory of your Hugo project. - Ensure content structure: Verify that your 
content/directory is structured logically.
For sections, use_index.mdfiles. - Create a theme partial: Create a file at 
layouts/partials/menu.htmlin your theme. This partial will iterate through the menu data generated by the script. 
layouts/partials/menu.html
{{ if .Site.Menus.main }}
  <ul>
    {{ range .Site.Menus.main }}
      {{ if .HasChildren }}
        <li class="has-children">
          <a href="{{ .PageRef }}">{{ .Name }}</a>
          <ul>
            {{ range .Children }}
              <li><a href="{{ .PageRef }}">{{ .Name }}</a></li>
            {{ end }}
          </ul>
        </li>
      {{ else }}
        <li><a href="{{ .PageRef }}">{{ .Name }}</a></li>
      {{ end }}
    {{ end }}
  </ul>
{{ end }}
Reference the partial: Include the partial in your theme’s header or layout file:
layouts/partials/header.html
<nav>
  {{ partial "menu.html" . }}
</nav>
Run the script: From your terminal, run the Python script.
python generate_menu.py
import os
import yaml
def generate_menu(content_path, menu_config):
    """
    Recursively builds a menu structure based on the content directory.
    """
    menu_items = []
    
    # Get a sorted list of directories and files
    items = sorted(os.listdir(content_path))
    
    # Process directories first
    for item in items:
        item_path = os.path.join(content_path, item)
        if os.path.isdir(item_path):
            # A directory is a section; it may have an _index.md
            section_index = os.path.join(item_path, '_index.md')
            if os.path.exists(section_index):
                # The section itself can be a menu item
                menu_item = {
                    'identifier': item,
                    'name': item.replace('-', ' ').title(),
                    'pageRef': f'/{item}',
                    'weight': 100, # A default weight for sections
                    'children': generate_menu(item_path, menu_config) # Recurse for nested sections
                }
                menu_items.append(menu_item)
    
    # Process single pages in the current directory
    for item in items:
        item_path = os.path.join(content_path, item)
        if os.path.isfile(item_path) and item.endswith('.md') and not item.startswith('_index'):
            # A regular markdown file is a menu page
            page_name = os.path.splitext(item)[0]
            menu_item = {
                'identifier': page_name,
                'name': page_name.replace('-', ' ').title(),
                'pageRef': f'/{os.path.basename(content_path)}/{page_name}',
                'weight': 200 # A default weight for pages
            }
            menu_items.append(menu_item)
    return menu_items
def build_menu_config(hugo_root_dir, menu_name='main'):
    """
    Builds the complete YAML menu configuration.
    """
    content_path = os.path.join(hugo_root_dir, 'content')
    if not os.path.exists(content_path):
        print("Error: 'content' directory not found.")
        return
        
    menu_data = generate_menu(content_path, {})
    
    # Add a Home menu item at the top
    home_menu = {
        'identifier': 'home',
        'name': 'Home',
        'pageRef': '/',
        'weight': 1
    }
    menu_data.insert(0, home_menu)
    
    # Format for Hugo's config structure
    final_config = {menu_name: menu_data}
    
    # Write to a menu.yaml file in config/_default/
    config_dir = os.path.join(hugo_root_dir, 'config', '_default')
    os.makedirs(config_dir, exist_ok=True)
    
    output_file = os.path.join(config_dir, 'menu.yaml')
    with open(output_file, 'w') as f:
        yaml.dump(final_config, f, sort_keys=False, allow_unicode=True)
    
    print(f"Successfully generated menu config at: {output_file}")
if __name__ == '__main__':
    # Set the root of your Hugo project
    hugo_root = os.path.dirname(os.path.abspath(__file__))
    build_menu_config(hugo_root)
