Learning Objectives
- Register menu locations in your theme
- Display menus with wp_nav_menu()
- Understand menu parameters and options
- Create custom Walker classes
- Build responsive mobile menus
- Style menus with CSS classes
- Handle fallback menus
Understanding WordPress Menus
WordPress menus allow users to create custom navigation without touching code. As a theme developer, you define where menus can appear (menu locations) and how they're displayed.
Key Concept
Menu Implementation Flow
1. Register
→
Define menu locations
2. Create
→
User creates menu
3. Assign
→
User assigns to location
4. Display
Theme displays menu
Registering Menu Locations
Basic Menu Registration
<?php
// In functions.php
function mytheme_register_menus() {
register_nav_menus( array(
'primary' => esc_html__( 'Primary Menu', 'mytheme' ),
'secondary' => esc_html__( 'Secondary Menu', 'mytheme' ),
'footer' => esc_html__( 'Footer Menu', 'mytheme' ),
'social' => esc_html__( 'Social Links Menu', 'mytheme' ),
'mobile' => esc_html__( 'Mobile Menu', 'mytheme' ),
) );
}
add_action( 'after_setup_theme', 'mytheme_register_menus' );
// Alternative: Register single menu
register_nav_menu( 'primary', __( 'Primary Menu', 'mytheme' ) );
Displaying Menus with wp_nav_menu()
Basic Menu Display
<?php
// Simple menu display
wp_nav_menu( array(
'theme_location' => 'primary',
) );
// Full parameters example
wp_nav_menu( array(
'theme_location' => 'primary', // Location registered
'menu' => '', // Menu name, ID, or slug
'menu_class' => 'nav-menu', // CSS class for ul element
'menu_id' => 'primary-menu', // ID for ul element
'container' => 'nav', // Container element
'container_class' => 'primary-navigation', // Container CSS class
'container_id' => 'site-navigation', // Container ID
'fallback_cb' => 'wp_page_menu', // Fallback function
'before' => '', // Before link text
'after' => '', // After link text
'link_before' => '', // Before link markup
'link_after' => '', // After link markup
'echo' => true, // Echo or return
'depth' => 0, // How many levels (0 = all)
'walker' => '', // Custom walker class
'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>',
) );
wp_nav_menu() Parameters Reference
| Parameter | Type | Default | Description |
|---|---|---|---|
theme_location |
string | '' | Registered menu location identifier |
menu |
string | '' | Menu name, ID, slug, or object |
container |
string | 'div' | Container element (nav, div, or false) |
container_class |
string | '' | CSS class for container |
container_id |
string | '' | ID for container |
menu_class |
string | 'menu' | CSS class for ul element |
menu_id |
string | '' | ID for ul element |
fallback_cb |
string | 'wp_page_menu' | Fallback function if menu doesn't exist |
depth |
int | 0 | How many hierarchy levels (0 = all) |
walker |
object | '' | Custom Walker class instance |
echo |
bool | true | Echo or return the menu |
Custom Walker Class
Create custom menu markup with a Walker class for advanced customization.
Simple Bootstrap Walker Example
<?php
/**
* Custom Nav Walker for Bootstrap
*/
class My_Walker_Nav_Menu extends Walker_Nav_Menu {
// Start Level
public function start_lvl( &$output, $depth = 0, $args = null ) {
$indent = str_repeat( "\t", $depth );
$output .= "\n$indent<ul class=\"dropdown-menu\">\n";
}
// Start Element
public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
$id = apply_filters( 'nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args );
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $class_names . '>';
$atts = array();
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
$item_output = $args->before;
$item_output .= '<a' . $attributes . '>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
// Usage
wp_nav_menu( array(
'theme_location' => 'primary',
'walker' => new My_Walker_Nav_Menu(),
) );
Menu Best Practices
Development Best Practices
- Always check if menu exists: Use has_nav_menu() before displaying
- Provide fallbacks: Show something useful when no menu is set
- Use semantic HTML: Use nav element for navigation
- Add ARIA labels: Improve accessibility with proper ARIA attributes
- Make it responsive: Ensure menus work on all devices
- Limit depth on mobile: Consider limiting menu depth for mobile
- Use descriptive location names: Make it clear where menus will appear
- Document menu locations: Help users understand menu purposes
Don't hardcode menu items in templates. Always use the WordPress menu system to allow user customization.
Practice Exercise
Build Complete Navigation System