wp_list_pages( array|string $args =  ): void|string

Retrieves or displays a list of pages (or hierarchical post type items) in list (li) format.

Description

See also

Parameters

$argsarray|stringoptional
Array or string of arguments to generate a list of pages. See get_pages() for additional arguments.
  • child_of int
    Display only the sub-pages of a single page by ID. Default 0 (all pages).
  • authors string
    Comma-separated list of author IDs. Default empty (all authors).
  • date_format string
    PHP date format to use for the listed pages. Relies on the 'show_date' parameter.
    Default is the value of 'date_format' option.
  • depth int
    Number of levels in the hierarchy of pages to include in the generated list.
    Accepts -1 (any depth), 0 (all pages), 1 (top-level pages only), and n (pages to the given n depth). Default 0.
  • echo bool
    Whether or not to echo the list of pages. Default true.
  • exclude string
    Comma-separated list of page IDs to exclude.
  • include array
    Comma-separated list of page IDs to include.
  • link_after string
    Text or HTML to follow the page link label. Default null.
  • link_before string
    Text or HTML to precede the page link label. Default null.
  • post_type string
    Post type to query for. Default 'page'.
  • post_status string|array
    Comma-separated list or array of post statuses to include. Default 'publish'.
  • show_date string
    Whether to display the page publish or modified date for each page. Accepts 'modified' or any other value. An empty value hides the date.
  • sort_column string
    Comma-separated list of column names to sort the pages by. Accepts 'post_author', 'post_date', 'post_title', 'post_name', 'post_modified', 'post_modified_gmt', 'menu_order', 'post_parent', 'ID', 'rand', or 'comment_count'. Default 'post_title'.
  • title_li string
    List heading. Passing a null or empty value will result in no heading, and the list will not be wrapped with unordered list <ul> tags. Default 'Pages'.
  • item_spacing string
    Whether to preserve whitespace within the menu’s HTML. Accepts 'preserve' or 'discard'.
    Default 'preserve'.
  • walker Walker
    Walker instance to use for listing pages. Default empty which results in a Walker_Page instance being used.
More Arguments from get_pages( … $args )Array or string of arguments to retrieve pages.
  • child_of int
    Page ID to return child and grandchild pages of. Note: The value of $hierarchical has no bearing on whether $child_of returns hierarchical results. Default 0, or no restriction.
  • sort_order string
    How to sort retrieved pages. Accepts 'ASC', 'DESC'. Default 'ASC'.
  • sort_column string
    What columns to sort pages by, comma-separated. Accepts 'post_author', 'post_date', 'post_title', 'post_name', 'post_modified', 'menu_order', 'post_modified_gmt', 'post_parent', 'ID', 'rand', 'comment*count'.
    'post*' can be omitted for any values that start with it.
    Default 'post_title'.
  • hierarchical bool
    Whether to return pages hierarchically. If false in conjunction with $child_of also being false, both arguments will be disregarded.
    Default true.
  • exclude int[]
    Array of page IDs to exclude.
  • include int[]
    Array of page IDs to include. Cannot be used with $child_of, $parent, $exclude, $meta_key, $meta_value, or $hierarchical.
  • meta_key string
    Only include pages with this meta key.
  • meta_value string
    Only include pages with this meta value. Requires $meta_key.
  • authors string
    A comma-separated list of author IDs.
  • parent int
    Page ID to return direct children of. Default -1, or no restriction.
  • exclude_tree string|int[]
    Comma-separated string or array of page IDs to exclude.
  • number int
    The number of pages to return. Default 0, or all pages.
  • offset int
    The number of pages to skip before returning. Requires $number.
    Default 0.
  • post_type string
    The post type to query. Default 'page'.
  • post_status string|array
    A comma-separated list or array of post statuses to include.
    Default 'publish'.

Default:''

Return

void|string Void if 'echo' argument is true, HTML list of pages if 'echo' is false.

More Information

The following classes are applied to menu items, i.e. to the HTML <li> tags, generated by wp_list_pages() . Note: The wp_list_pages() and wp_page_menu() functions output the same CSS classes.

All Menu Items

  • .page_item
    This class is added to menu items that correspond to a static page.
  • .page-item-$ID
    This class is added to menu items that correspond to a static page, where $ID is the static page ID.

Current-Page Menu Items

  • .current_page_item
    This class is added to menu items that correspond to the currently rendered static page.

Current-Page Parent Menu Items

  • .current_page_parent
    This class is added to menu items that correspond to the hierarchical parent of the currently rendered static page.

Current-Page Ancestor Menu Items

  • .current_page_ancestor
    This class is added to menu items that correspond to a hierarchical ancestor of the currently rendered static page.

Source

function wp_list_pages( $args = '' ) {
	$defaults = array(
		'depth'        => 0,
		'show_date'    => '',
		'date_format'  => get_option( 'date_format' ),
		'child_of'     => 0,
		'exclude'      => '',
		'title_li'     => __( 'Pages' ),
		'echo'         => 1,
		'authors'      => '',
		'sort_column'  => 'menu_order, post_title',
		'link_before'  => '',
		'link_after'   => '',
		'item_spacing' => 'preserve',
		'walker'       => '',
	);

	$parsed_args = wp_parse_args( $args, $defaults );

	if ( ! in_array( $parsed_args['item_spacing'], array( 'preserve', 'discard' ), true ) ) {
		// Invalid value, fall back to default.
		$parsed_args['item_spacing'] = $defaults['item_spacing'];
	}

	$output       = '';
	$current_page = 0;

	// Sanitize, mostly to keep spaces out.
	$parsed_args['exclude'] = preg_replace( '/[^0-9,]/', '', $parsed_args['exclude'] );

	// Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array).
	$exclude_array = ( $parsed_args['exclude'] ) ? explode( ',', $parsed_args['exclude'] ) : array();

	/**
	 * Filters the array of pages to exclude from the pages list.
	 *
	 * @since 2.1.0
	 *
	 * @param string[] $exclude_array An array of page IDs to exclude.
	 */
	$parsed_args['exclude'] = implode( ',', apply_filters( 'wp_list_pages_excludes', $exclude_array ) );

	$parsed_args['hierarchical'] = 0;

	// Query pages.
	$pages = get_pages( $parsed_args );

	if ( ! empty( $pages ) ) {
		if ( $parsed_args['title_li'] ) {
			$output .= '<li class="pagenav">' . $parsed_args['title_li'] . '<ul>';
		}
		global $wp_query;
		if ( is_page() || is_attachment() || $wp_query->is_posts_page ) {
			$current_page = get_queried_object_id();
		} elseif ( is_singular() ) {
			$queried_object = get_queried_object();
			if ( is_post_type_hierarchical( $queried_object->post_type ) ) {
				$current_page = $queried_object->ID;
			}
		}

		$output .= walk_page_tree( $pages, $parsed_args['depth'], $current_page, $parsed_args );

		if ( $parsed_args['title_li'] ) {
			$output .= '</ul></li>';
		}
	}

	/**
	 * Filters the HTML output of the pages to list.
	 *
	 * @since 1.5.1
	 * @since 4.4.0 `$pages` added as arguments.
	 *
	 * @see wp_list_pages()
	 *
	 * @param string    $output      HTML output of the pages list.
	 * @param array     $parsed_args An array of page-listing arguments. See wp_list_pages()
	 *                               for information on accepted arguments.
	 * @param WP_Post[] $pages       Array of the page objects.
	 */
	$html = apply_filters( 'wp_list_pages', $output, $parsed_args, $pages );

	if ( $parsed_args['echo'] ) {
		echo $html;
	} else {
		return $html;
	}
}

Hooks

apply_filters( ‘wp_list_pages’, string $output, array $parsed_args, WP_Post[] $pages )

Filters the HTML output of the pages to list.

apply_filters( ‘wp_list_pages_excludes’, string[] $exclude_array )

Filters the array of pages to exclude from the pages list.

Changelog

VersionDescription
4.7.0Added the item_spacing argument.
1.5.0Introduced.

User Contributed Notes

  1. Skip to note 18 content

    List Sub-Pages
    Versions prior to WordPress 2.0.1:

    Put this inside the the_post() section of the page.php template of your WordPress theme after the_content(), or put it in a copy of the page.php template that you use for pages that have sub-pages:

    <ul>
    	<?php
    	global $id;
    	wp_list_pages( array(
    		'title_li'    => '',
    		'child_of'    => $id,
    		'show_date'   => 'modified',
    		'date_format' => $date_format
    	) );
    	?>
    </ul>

    This example does not work with WordPress 2.0.1 or newer if placed in a page template because the global $id is not set. Use the following code instead.

    WordPress 2.0.1 or newer:

    NOTE: Requires an HTML list tag (either <ul> or <ol>) even if there are no subpages. Keep this in mind if you are using CSS to style the list.

    <ul>
    	<?php
    	wp_list_pages( array(
    		'title_li'    => '',
    		'child_of'    => $post->ID,
    		'show_date'   => 'modified',
    		'date_format' => $date_format
    	) );
    	?>
    </ul>

    The following example will generate a list only if there are child pages (pages that designate the current page as a parent) for the current page:

    <?php
    $children = wp_list_pages( 'title_li=&child_of='.$post->ID.'&echo=0' );
    if ( $children) : ?>
    	<ul>
    		<?php echo $children; ?>
    	</ul>
    <?php endif; ?>
  2. Skip to note 19 content

    Hiding or Changing the List Heading
    The default heading of the list (“Pages”) of Pages generated by wp_list_pages can be hidden by passing a null or empty value for the title_li parameter. The following example displays no heading text above the list.

    <ul>
    <?php wp_list_pages( array( 'title_li' => '' ) ); ?>
    </ul>

    In the following example, only Pages with IDs 9, 5, and 23 are included in the list and the heading text has been changed to the word “Poetry”, with a heading style of :

    <ul>
    <?php wp_list_pages( array(
    	'include'  => array( 5, 9, 23 ),
    	'title_li' => '<h2>' . __('Poetry') . '</h2>'
    ) ); ?>
    </ul>
  3. Skip to note 20 content

    List members of a custom post type
    If a given custom post type is hierarchical in nature, then wp_list_pages() can be used to list the member of that custom post type. In this example the custom post type Portfolio is listed:

    $args = array(
    	'post_type' => 'portfolio',
    	'title_li'  => __( 'Portfolio', 'textdomain' )
    );
    wp_list_pages( $args ); 
  4. Skip to note 21 content

    I didn’t see anywhere that you could pass a sort_order argument. So I think it would be good to make it more obvious by including it in the args list.

    This code orders pages by post_date and sorts the pages by descending order so that you will get the latest pages first.

    wp_list_pages(array('sort_column' => 'post_date', 'sort_order' => 'desc'));
  5. Skip to note 22 content

    List subpages even if on a subpage

    The above examples will only show the children from the parent page, but not when actually on a child page. This code will show the child pages, and only the child pages, when on a parent or on one of the children.

    This code will not work if placed after a widget block in the sidebar.

    <?php
    if ( $post->post_parent ) {
    	$children = wp_list_pages( array(
    		'title_li' => '',
    		'child_of' => $post->post_parent,
    		'echo'     => 0
    	) );
    } else {
    	$children = wp_list_pages( array(
    		'title_li' => '',
    		'child_of' => $post->ID,
    		'echo'     => 0
    	) );
    }
    
    if ( $children ) : ?>
    	<ul>
    		<?php echo $children; ?>
    	</ul>
    <?php endif; ?>

    As an alternative, this code in a sidebar.php, displays only top level pages, but when viewing a page that has children (or is a child) it displays only children of that parent.

    When visiting main page, all top level pages are listed in the sidebar.
    When visiting a top level page with no children, all top level pages are listed.
    When visiting a top level page with children, just the children pages, and descendant pages, are listed.
    When visiting a child page, just the children, and descendant pages, of that parent, are listed.

    $output = wp_list_pages( array(
    	'echo'     => 0,
    	'depth'    => 1,
    	'title_li' => '<h2>' . __( 'Top Level Pages', 'textdomain' ) . '</h2>'
    ) );
    
    if ( is_page() ) {
    	$page = $post->ID;
    	if ( $post->post_parent ) {
    		$page = $post->post_parent;
    	}
    
    	$children = wp_list_pages( array(
    		'echo'     => 0,
    		'child_of' => $page,
    		'title_li' => ''
    	) );
    
    	if ( $children ) {
    		$output = wp_list_pages( array(
    			'echo' => 0,
    			'child_of' => $page,
    			'title_li' => '<h2>' . __( 'Child Pages', 'textdomain' ) . '</h2>'
    		) );
    	}
    }
    echo $output;

    Page list that only displays if child (sub) pages exist, displays page list of subpages on the parent page AND on the child pages
    However this code keeps the parent page name in the title which makes it different from the previous example.

    <?php
    if ( $post->post_parent ) {
    	$children = wp_list_pages( array(
    		'title_li' => '',
    		'child_of' => $post->post_parent,
    		'echo'     => 0
    	) );
    	$title = get_the_title( $post->post_parent );
    } else {
    	$children = wp_list_pages( array(
    		'title_li' => '',
    		'child_of' => $post->ID,
    		'echo'     => 0
    	) );
    	$title = get_the_title( $post->ID );
    }
    
    if ( $children ) : ?>
    	<h2><?php echo $title; ?></h2>
    	<ul>
    		<?php echo $children; ?>
    	</ul>
    <?php endif; ?>
  6. Skip to note 23 content

    Markup and styling of page items
    By default, wp_list_pages() generates a nested, unordered list of WordPress pages created with the Write > Page admin panel. You can remove the outermost item (li.pagenav) and list (ul) by setting the title_li parameter to an empty string.

    All list items (li) generated by wp_list_pages() are marked with the class page_item. When wp_list_pages() is called while displaying a page, the list item for that Page is given the additional class current_page_item.

    <li class="pagenav">
    Pages [title_li]
    	<ul>
    	<!-- Output starts here if 'title_li' parameter is empty -->
    		<li class="page-item-2 page_item current_page_ancestor current_page_parent">
    		[parent of the current Page]
    			<ul>
    				<li class="page-item-21 page_item current_page_item">
    					[the current Page]
    				</li>
    			</ul>
    		</li>
    		<li class="page-item-3 page_item">
    			[another Page]
    		</li>
    	</ul>
    </li>

    They can be styled with CSS selectors:
    [html]
    <style type="text/css">
    .pagenav { … } /* the outermost list item; contains whole list */
    .page-item-2 { … } /* item for Page ID 2 */
    .page_item { … } /* any Page item */
    .current_page_item { … } /* the current Page */
    .current_page_parent { … } /* parent of the current Page */
    .current_page_ancestor { … } /* any ancestor of the current Page */
    </style>
    In order to achieve an accordion menu effect for instance, the following CSS can be used:

    <style type="text/css">
    .pagenav ul ul,
    .pagenav .current_page_item ul ul,
    .pagenav .current_page_ancestor ul ul,
    .pagenav .current_page_ancestor .current_page_item ul ul,
    .pagenav .current_page_ancestor .current_page_ancestor ul ul {
    display: none;
    }

    .pagenav .current_page_item ul,
    .pagenav .current_page_ancestor ul,
    .pagenav .current_page_ancestor .current_page_item ul,
    .pagenav .current_page_ancestor .current_page_ancestor ul,
    .pagenav .current_page_ancestor .current_page_ancestor .current_page_item ul,
    .pagenav .current_page_ancestor .current_page_ancestor .current_page_ancestor ul {
    display: block;
    }
    </style>
    [/html]

  7. Skip to note 25 content

    List all top-level pages and subpages only of this parent

    The following lists all top-level pages and

    if on a top level page – children of only this page
    if on a subpage – its siblings but not children of other top pages

    $ancestor_id = 1843; // you want to change this
    $descendants = get_pages( array( 'child_of' => $ancestor_id ) );
    $incl = '';
    
    foreach ( $descendants as $page ) {
    	if ( ( $page->post_parent == $ancestor_id )
    		|| ( $page->post_parent == $post->post_parent )
    		|| ( $page->post_parent == $post->ID )
    	) {
    		$incl .= $page->ID . ",";
    	}
    }
    ?>
    
    <ul>
    	<?php
    	wp_list_pages( array(
    		'child_of'    => $ancestor_id,
    		'include'     => $incl,
    		'link_before' => '',
    		'title_li'    => '',
    		'sort_column' => 'menu_order'
    	) );
    	?>
    </ul>
  8. Skip to note 26 content

    *Custom Post/Page Types*

    Custom page types will require the hierarchical property to be set to true in the register_post_type (see https://developer.wordpress.org/reference/functions/register_post_type/) parameter list.

    By default the only WordPress post type that is hierarchical is the ‘page’ type.

    Only hierarchical post types are returned by this function.

  9. Skip to note 27 content

    List Pages by Page Order
    The following example lists the pages in the order defined by the Order setting for each page in the PagesEdit panel.

    <ul>
    	<?php wp_list_pages( array( 'sort_column' => 'menu_order' ) ); ?>
    </ul>

    If you wanted to sort the list by page order and display the word “Prose” as the list heading (in h2 style) on a sidebar, you could add the following code to the sidebar.php file:

    <ul>
    	<?php wp_list_pages( 'sort_column=menu_order&title_li=<h2>' . __( 'Prose' ) . '</h2>' ); ?>
    </ul>

    Using the following piece of code, the pages will display without heading and in page order:

    <ul>
    	<?php wp_list_pages( 'sort_column=menu_order&title_li=' ); ?>
    </ul>
  10. Skip to note 30 content

    List whole subpages

    This is how to get the whole subpages list

    if( ! $post->post_parent ) {
    	// Will display the subpages of this top level page.
    	$children = wp_list_pages( array(
    		'title_li' => '',
    		'child_of' => $post->ID,
    		'echo'     => 0
    	) );
    } else {
    	if ( $post->ancestors ) {
    		/*
    		 * Now you can get the the top ID of this page. WordPress is putting the ids DESC,
    		 * thats why the top level ID is the last one.
    		 */
    		$ancestors = get_post_ancestors( $this_page );
    		$children  = wp_list_pages( array(
    			'title_li' => '',
    			'child_of' => $ancestors,
    			'echo'     => 0
    		) );
    	}
    }
    
    if ( $children ) : ?>
    	<ul>
    		<?php echo $children; ?>
    	</ul>
    <?php endif; ?>
  11. Skip to note 31 content

    List parent page and all descendant pages

    Since there’s no way to tell wp_list_pages to display a particular parent tree (parent and all generations displayes) this example uses get_pages to get all the descendants for a parent, then use wp_list_pages to display the ‘family tree’.

    <?php 
    // Use wp_list_pages to display parent and all child pages all generations (a tree with parent).
    $parent = 93;
    $args   = array( 'child_of' => $parent );
    $pages  = get_pages( $args );
    
    if ( $pages ) {
    	$pageids = array();
    	foreach ( $pages as $page ) {
    		$pageids[] = $page->ID;
    	}
    
    	$args = array(
    		'title_li' => sprintf( __( 'Tree of Parent Page %s', 'textdomain' ), $parent ),
    		'include'  =>  $parent . ',' . implode( ",", $pageids )
    	);
    	wp_list_pages( $args );
    }
    ?>
  12. Skip to note 32 content

    List current Page with its ancestors and children

    This example will list current page, ancestors of current page, and children of current page, Since child_of displays all children, wpdb is used instead, along with include to not display everything ‘and the kitchen sink’.

    // If the post has a parent.
    if( $post->post_parent ) {
    	// Collect ancestor pages.
    	$relations = get_post_ancestors( $post->ID );
    
    	// Get child pages.
    	$result = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM wp_posts WHERE post_parent = %s AND post_type = %s", $post->ID, 'page' ) );
    	if ( $result ) {
    		foreach ( $result as $page ) {
    			array_push( $relations, $page->ID );
    		}
    	}
    
    	// Add current post to pages.
    	array_push( $relations, $post->ID );
    
    	// Get comma delimited list of children and parents and self.
    	$relations_string = implode( ",",$relations );
    
    	// Use include to list only the collected pages.
    	$sidelinks = wp_list_pages( array(
    		'title_li' => '',
    		'echo'     => 0,
    		'include'  => $relations_string
    	) );
    } else {
    	// Display only main level and children.
    	$sidelinks = wp_list_pages( array(
    		'title_li' => '',
    		'echo'     => 0,
    		'depth'    => 1,
    		'child_of' => $post->ID
    	) );
    }
    
    if ( $sidelinks ) : ?>
    	<h2><?php the_title(); ?></h2>
    	<ul>
    		<?php
    		// Links in <li> tags.
    		echo $sidelinks;
    		?>
    	</ul>         
    <?php endif; ?>
  13. Skip to note 33 content

    List topmost ancestor and its immediate children

    This method will show the topmost ancestor of the current page as well as the topmost ancestor’s immediate children. This can be used for clean secondary subnavigation.

    First, create the following function (preferably in functions.php, assuming this is for a theme):

    if ( ! function_exists( 'wpdocs_get_post_top_ancestor_id' ) ) {
    /**
     * Gets the id of the topmost ancestor of the current page.
     *
     * Returns the current page's id if there is no parent.
     * 
     * @return int ID of the top ancestor page.
     */
    function wpdocs_get_post_top_ancestor_id() {
    	if ( ! $post = get_post() ) {
    		return;
    	}
        
    	$top_ancestor = $post->ID;
        if ( $post->post_parent ) {
            $ancestors = array_reverse( get_post_ancestors( $post->ID ) );
            $top_ancestor = $ancestors[0];
        }
        
        return $top_ancestor;
    }
    } // Exists.

    Next, add the following code to your theme (wherever you want the menu to appear):

    <ul class="clearfix">
    	<?php
    	wp_list_pages( array(
    		'title_li' => '',
    		'include'  => wdocs_get_post_top_ancestor_id()
    	) );
    	
    	wp_list_pages( array(
    		'title_li' => '',
    		'depth'    => 1,
    		'child_of' => wpdocs_get_post_top_ancestor_id()
    	) );
    	?>
    </ul>    

You must log in before being able to contribute a note or feedback.