How to build a custom Walker for WordPress menus
n WordPress, custom menus are essential when developing advanced themes. While the wp_nav_menu()function is flexible, there are cases where you need full control over the generated HTML. For those scenarios, WordPress provides the Walker_Nav_Menuclass, which you can extend to create a custom walker.
In this guide, we’ll cover:
- What is a Walker in WordPress?
- When to use a custom walker
- How to build your own walker step by step
- How to apply it to your menu
- Full working example
What is a Walker in WordPress?
A Walker is a class used by WordPress to loop through hierarchical data structures (like menus or nested taxonomies) and generate the appropriate HTML.
When using wp_nav_menu(), WordPress uses the Walker_Nav_Menuclass to create the HTML structure for <ul>, <li>, and submenus.
To customize this structure, you can extend Walker_Nav_Menuand override certain methods to change the output.
When should you use a custom Walker?
You should create a custom walker when:
- You need to add custom classes or attributes to menu items.
- You want full control over the HTML structure of menus.
- You’re using a CSS framework like Bootstrap or Tailwind and need specific markup.
- You want to insert icons,
<div>s, or custom structures inside the menu.
Key methods to override
When extending Walker_Nav_Menu, these are the most commonly overridden methods:
<?php start_lvl() // Starts a submenu (<ul>) end_lvl() // Ends a submenu start_el() // Starts a menu item (<li>) end_el() // Ends a menu item
How to create a custom Walker
Create a file called CustomMenuWalker.phpinside your theme or a plugin.
<?php
class CustomMenuWalker extends Walker_Nav_Menu {
public function startLvl( &$output, $depth = 0, $args = [] ) {
$indent = str_repeat( "\t", $depth );
$output .= "\n$indent<ul class=\"submenu-level-$depth\">\n";
}
public function endLvl( &$output, $depth = 0, $args = [] ) {
$indent = str_repeat( "\t", $depth );
$output .= "$indent</ul>\n";
}
public function startEl( &$output, $item, $depth = 0, $args = [], $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$classNames = implode( ' ', $item->classes );
$classAttr = $classNames ? ' class="' . esc_attr( $classNames ) . '"' : '';
$output .= "$indent<li$classAttr>";
$title = apply_filters( 'the_title', $item->title, $item->ID );
$attributes = ' href="' . esc_attr( $item->url ) . '"';
$output .= '<a' . $attributes . '>';
$output .= esc_html( $title );
$output .= '</a>';
}
public function endEl( &$output, $item, $depth = 0, $args = [] ) {
$output .= "</li>\n";
}
}
How to use the Walker in wp_nav_menu()
Inside header.phpor wherever you output the menu:
<?php
wp_nav_menu([
'theme_location' => 'main_menu',
'container' => false,
'menu_class' => 'main-nav',
'walker' => new CustomMenuWalker()
]);
?>
How to register the menu
In your functions.phpfile or a plugin:
<?php
function registerThemeMenus() {
register_nav_menus([
'main_menu' => 'Main Menu'
]);
}
add_action( 'after_setup_theme', 'registerThemeMenus' );
Suggested file structure
/wp-content/themes/your-theme/ │ ├── functions.php ├── header.php ├── inc/ │ └── CustomMenuWalker.php
And inside functions.php, include the class:
<?php require_once get_template_directory() . '/inc/CustomMenuWalker.php';
Example: Walker for Bootstrap 5
If you’re using Bootstrap, submenu <ul>elements need the class dropdown-menu, and the parent <li>elements should use the class dropdownwith data-bs-toggle.
Here’s a custom startEl()for Bootstrap:
<?php
public function startEl( &$output, $item, $depth = 0, $args = [], $id = 0 ) {
$hasChildren = in_array( 'menu-item-has-children', $item->classes );
$classes = $hasChildren ? 'dropdown' : '';
$output .= '<li class="nav-item ' . $classes . '">';
$linkClass = $hasChildren ? 'nav-link dropdown-toggle' : 'nav-link';
$attrs = 'class="' . $linkClass . '"';
$attrs .= $hasChildren ? ' data-bs-toggle="dropdown" role="button" aria-expanded="false"' : '';
$attrs .= ' href="' . esc_url( $item->url ) . '"';
$output .= '<a ' . $attrs . '>' . esc_html( $item->title ) . '</a>';
}
Extra tips
- Always use
esc_html()andesc_attr()for security. - Use the
$depthparameter to customize submenu levels differently. - Test your walker with different themes and CSS classes.
Creating a custom walker in WordPress gives you full control over the HTML structure of your menus. It’s a powerful tool when working with custom designs or CSS frameworks.




