Building a custom WordPress menu

Menu’s are one of the most important aspects of a site. Choosing the right elements, the positioning, it can make a huge difference in the user experience. In WordPress there is a menu system, which you’re probably familiar with already. It enables you to build the menu structure through the WP Admin, under WP Admin > Appearance > Menu. Most themes then provide menu location(s), and most have at least a few. There are also many plugins that provide various forms of menu integration and customization.

If you rely entirely on themes and plugins to provide menu handling for you, then you miss out on the opportunity to create custom menus. For instance although many “mega menu” plugins exist, it can be very difficult to get the exact result you might want using 3rd party solutions.

The key to custom menus is first having solid HTML5/CSS skills, and then learning the PHP needed to render custom menus in a unique way, and finally a dash of Javascript and/or jQuery is usually needed as well. This is mainly a front-end development project, but because it can involve some PHP as well we can term it to require full-stack skills.

It’s important to understand that a Menu and a Menu Location are 2 different things in WordPress. Themes are required to “register locations” rather than specifying menu’s directly. This makes themes more versatile because admin users can make any menu they want and then drop it into a location for display. In practice however the relationship between a menu and it’s location is almost always 1:1 because when would you ever want to stack a menu on top of another menu? Even in an area like the footer where you might want multiple menu’s to display, odds are these will be separate menu’s in separate locations because you need some markup around each one, like for instance the common approach of have multiple columns with a vertical menu in each.

The function we use in the theme to register nav menus is register_nav_menus(). This function takes an array as it’s only argument, and this array should be an associative array where the keys represent the menu location ID and the value is the display label. Notice the display labels are wrapped in the localization function __() to enable translation.

register_nav_menus( array(
  'primary_menu' => __( 'Primary Menu', 'text_domain' ),
  'footer_menu'  => __( 'Footer Menu', 'text_domain' ),

In practice “text_domain” should be replaced with the text domain for your theme which is a unique string, often a lowercased form of the theme name. This is used by translators to target your theme’s text.

Rendering Menu’s with wp_nav_menu()

The next function we’ll use is wp_nav_menu() which is used to render menu locations. A minimal example is shown below:

wp_nav_menu( array( 'theme_location' => 'header-menu' ) );

Notice that ‘theme_location’ is specified inside an array. There are many other array arguments that can be used in this display function. This allows themes to do customization to the the render of the navigation menu, but the majority of the HTML and CSS classes are still automatically rendered by WordPress. Which is why later we’ll cover making a custom Menu Walker Class, which will give us full control over the output. For many situation however, simply rendering the menu will be sufficient because you can create the styling needed for basic menus entirely with CSS and just by implementing the various classes that WordPress applies to the menu markup.

When you test rendering of your menu watch out for the automated menu that WordPress will generate if you use the wrong menu location key, or if no menu is attached to the location. This menu would have any pages you’ve created automatically added, and sometimes you might not notice the difference so it is worth building your menu with specific items that you can identify in a test.

Let’s take a look at what a mostly unstyled menu might look like from our own project here at this was our primary menu being rendered in the header:

A perfect website menu, end of tutorial. Thank you for joining us.

About the Instructor

Casey Milne

Senior WordPress Developer, Eat/Build/Play

Casey started programming PHP in 2003. His early work involved PHP application development and custom sites built from PHP and HTML. He adopted Drupal in the early days of CMS, and then switched later to WordPress in 2012. For the past 8-years he has developed WordPress plugins and large-scale WP sites. Casey's main forte is handling API integrations and building data-driven functionality in WordPress.