How To Use wp_list_pages() To Create Parent and Child Page Menus

Click to share! ⬇️

Create Parent and Child Page Menus

In this tutorial let’s look at how we can handle working with links to and from parent and child pages in your themes. You might see these hierarchies referred to as top-level pages and subpages. WordPress makes it easy to link pages on your site in such a way so that your visitors can easily understand the hierarchy and relationship between them. We’ll make use of the wp_list_pages() function to set up a list of child pages based on the particular parent page we have selected. Let’s take a look at how to set this up now.


Parent and Child Pages in the WordPress Dashboard

For example, you might have a Services page and on that page is information about all the services you can provide. You might even have enough content to break out some of the services themselves onto their own page. In this case here, we have added a Consulting Child page and a Technical Support child page to the parent page of Services. WordPress indicates this visually by indenting the child page just slightly as well as putting a leading dash before the child page name.
example of child pages in wordpress


How to set the parent page of a child

It is very easy to assign a parent to a page when you create a new page in WordPress. On any given page during creation or editing, if we look at the Page Attributes section, we can see a drop-down menu to choose which top-level page we would like to have be the parent of this current page we are working on. Here we will go ahead and set the Consulting page to have a Parent of Services.
setting a wordpress page as a parent of a child


The Desired Navigation

Now that we have a few child pages set up, we will have a goal to make WordPress automatically output child links to the page depending on what parent page we are visiting. So if we visit the WordPress Example Page we have set up, we should see no child links. If we visit the Services page, however, we should see two links to the Child Pages of Consulting and Technical Support. If you are following along, be sure to add the Parent of your two child pages to the menu we have been working on so far.
Adding parent level page to main menu


Introducing the wp_list_pages() WordPress Function

To help us achieve our goal, we will make use of the wp_list_pages() function. This function displays a list of pages from your site in list format. It automatically generates the <li> items for you. That is one thing that you often see with WordPress functions, is that they not only return values, but they actually output HTML. This is an important piece of information to remember. So let’s go ahead and just add this function to our page.php file and see what happens.

<?php

get_header();

if ( have_posts() ) :
	while ( have_posts() ) : the_post(); ?>

        <article class="page-layout">
            <?php wp_list_pages(); ?>
            <h2><?php the_title() ?></h2>
			<?php the_content() ?>
        </article>
	
	<?php endwhile;

else :
	echo '<p>There are no pages!</p>';
endif;

get_footer();

?>

When we now visit the Services page, we get the following output.
visiting our parent page

If we look at the source of the snippet generated by this function, it looks like this:

<li class="pagenav">Pages
  <ul>
    <li class="page_item page-item-47"><a href="http://wordpresstutorial.dev/about-us/">About Us</a></li>
    <li class="page_item page-item-51 page_item_has_children current_page_item"><a href="http://wordpresstutorial.dev/services/">Services</a>
      <ul class='children'>
        <li class="page_item page-item-53"><a href="http://wordpresstutorial.dev/services/consulting/">Consulting</a></li>
        <li class="page_item page-item-55"><a href="http://wordpresstutorial.dev/services/technical-support/">Technical Support</a></li>
      </ul>
    </li>
    <li class="page_item page-item-2"><a href="http://wordpresstutorial.dev/wordpress-example-page/">WordPress Example Page</a></li>
  </ul>
</li>

This is very interesting. Not only does the function output the HTML required to output the links in list form, but it also intelligently applies classes to the <ul> and <li> tags.


Customize The Output of wp_list_pages()

The default behavior of wp_list_pages() is to output links to all of the pages on your site as we just saw. In our case, we only want child links of a specific parent. To do this, we will need to alter the behavior of wp_list_pages() by passing in some arguments to the function. Let’s see what we can do. We only want to display child pages. In addition, we want to get rid of the Pages text that labels the links. Here are the arguments we can pass as an array to do this.

<?php

get_header();

if ( have_posts() ) :
	while ( have_posts() ) : the_post(); ?>

        <article class="page-layout">
			<?php $args = [
				'child_of' => $post->ID,
				'title_li' => '',
			];
			wp_list_pages( $args ); ?>
            <h2><?php the_title() ?></h2>
			<?php the_content() ?>
        </article>
	
	<?php endwhile;

else :
	echo '<p>There are no pages!</p>';
endif;

get_footer();

?>

child pages in action
Now, this gets us closer to what we want, but it is not just perfect yet. Notice that when we click the actual child links, they go away. What would be better is if we could click the child links, and the parent link would stay active, in addition to still offering the child links. We can do this, but we need to add some custom code to handle this for us. First, we need to add a new function to our functions.php file. This function’s purpose is to find the topmost parent ancestor of the currently viewed page and its name will be get_the_top_ancestor_id.

functions.php

<?php

function custom_theme_assets() {
	wp_enqueue_style( 'style', get_stylesheet_uri() );
}

add_action( 'wp_enqueue_scripts', 'custom_theme_assets' );

register_nav_menus( [ 'primary' => __( 'Primary Menu' ) ] );

function get_the_top_ancestor_id() {
	global $post;
	if ( $post->post_parent ) {
		$ancestors = array_reverse( get_post_ancestors( $post->ID ) );
		return $ancestors[0];
	} else {
		return $post->ID;
	}
}

We now want to make use of this function in our page.php file like so:
page.php

<?php

get_header();

if ( have_posts() ) :
	while ( have_posts() ) : the_post(); ?>

        <article class="page-layout">
			<?php $args = [
				'child_of' => get_the_top_ancestor_id(),
				'title_li' => '',
			];
			wp_list_pages( $args ); ?>
            <h2><?php the_title() ?></h2>
			<?php the_content() ?>
        </article>
	
	<?php endwhile;

else :
	echo '<p>There are no pages!</p>';
endif;

get_footer();

?>

Now if this is working properly, our child links should stay intact even when we click them. Let’s see how it works.
child links display properly

Very Cool! The child links now continue to display even when we drill down on them. Something is not quite right yet though. Did you notice that the ancestor page does not stay lit up so to speak? In other words, the Services tab no longer has a nice blue background to indicate that it is the active ancestor page. We can fix this easily by adding the current-page-ancestor class to our style.css stylesheet like so.

.navigation-menu ul li.current-menu-item a:link,
.navigation-menu ul li.current-menu-item a:visited,
.navigation-menu ul li.current-page-ancestor a:link,
.navigation-menu ul li.current-page-ancestor a:visited {
    background-color: #4285f4;
    color: #ffffff;
}

With that simple addition, our ancestor page should now stay active as we click on its child pages.
apply css to ancestor parent page


Applying Style To The Active Children Links

The final thing we will do is to apply the ‘lit up’ effect to the child pages as well. WordPress is very capable in this regard. It gives us a way to track not only the parent active state but the child’s active state at the same time. We’ll add a few more styling effects to the children’s links as well. Here is the styling we can apply to the child links in our style.css file.

.child-navigation-menu ul {
    margin: 20px;
    padding: 0;
}

.child-navigation-menu ul:before, .navigation-menu ul:after {
    content: "";
    display: table;
}

.child-navigation-menu ul li {
    list-style: none;
    float: left;
    margin-right: 3px;
}

.child-navigation-menu ul li {
    margin-right: 20px;
    font-size:80%;
}

.child-navigation-menu ul li a:link,
.child-navigation-menu ul li a:visited {
    text-decoration: none;
}

.child-navigation-menu ul li.current_page_item a:link,
.child-navigation-menu ul li.current_page_item a:visited {
    border-bottom: solid 3px #ecf0f1;
}

Check it out:
parent and child active state links
Pretty Sweet! Now, both the parent and child links are keeping track of their state and the UI presents different stylistic effects based on the current page the site visitor is viewing.


How To Use wp_list_pages() To Create Parent and Child Page Menus Summary

In this tutorial, we learned some cool features about working with page menus in WordPress. First off we saw how to set up and recognize parent and child page types in the WordPress Dashboard and why we might want to make use of them. From there, we translated that into what the User Interface should present based on whether a page was a parent or a child. The wp_list_pages() function helped us to do the heavy lifting in this regard. Once again we saw that when working with WordPress, we are going to have to be aware of which functions can help us, as well as how to customize their behavior based on the arguments that we pass to them.

Click to share! ⬇️