Using Different Walker Menu Classes for Header and Footer in WordPress

Header and footer navigation menus often require different markup structures — the header might use a multi-level dropdown while the footer renders a flat inline list. If you are already using a custom Walker_Nav_Menu, passing the menu location as a constructor argument lets you branch the output inside a single Walker class.

Problem: A theme registers the same navigation menu in both the header and footer, but each location needs different HTML structure or CSS classes — horizontal links in the header, a stacked list in the footer.

Solution: Create two Walker_Nav_Menu subclasses that output the desired markup, then pass each as the walker argument in the respective wp_nav_menu() call for the header and footer locations.

Step 1. Register both menu locations in functions.php:

<?php
register_nav_menus( [
    'primary'   => __( 'Primary Navigation', 'theme-name' ),
    'secondary' => __( 'Footer Navigation', 'theme-name' ),
] );

Step 2. When calling wp_nav_menu(), instantiate the Walker with the location string as a constructor argument:

<?php
// In header.php
if ( has_nav_menu( 'primary' ) ) {
    wp_nav_menu( [
        'container'      => false,
        'theme_location' => 'primary',
        'menu_id'        => 'nav',
        'menu_class'     => 'nav navbar-nav',
        'items_wrap'     => '<ul id="%1$s" class="%2$s">%3$s</ul>',
        'walker'         => new Custom_Walker_Nav_Menu( 'primary' ),
    ] );
}

Step 3. In your Walker class, accept the location in the constructor and use it inside start_el() to vary the output:

<?php
class Custom_Walker_Nav_Menu extends Walker_Nav_Menu {

    public $menu_location = 'primary';

    public function __construct( $menu_location = 'primary' ) {
        $this->menu_location = $menu_location;
    }

    public function start_el( &$output, $item, $depth = 0, $args = [], $id = 0 ) {

        // Way 1: get the full menu object for the current location
        $locations = get_nav_menu_locations();
        $menu      = wp_get_nav_menu_object( $locations[ $this->menu_location ] );

        // Way 2: use the location string directly
        $location = $this->menu_location;

        if ( $location === 'primary' ) {
            // Render header-specific markup
        } else {
            // Render footer-specific markup
        }
    }
}

This pattern keeps your Walker DRY — one class handles all locations — while still allowing completely different markup per location.

NOTE: If the markup difference between locations is significant, consider two separate Walker classes instead of branching inside one. A single large class with many conditionals can become hard to maintain as the theme grows.