I tried adding this to a similar much older thread but didn't get a response so I am posting it as a new question.

I have a large page based website that I am working on. Since it is mainly page driven, I used a function to allow categories and tags on pages. I would love to know if there is a way to exclude a category using the wp_list_pages() function. This would allow me to put in one id or slug rather than updating it with a new page id each time I have a new page that I want to exclude.

To be even more specific, I am using the following function which uses the wp_list_pages() function within it.

function wpb_list_child_pages() { 

global $post; 

if ( is_page() && $post->post_parent )

    $childpages = wp_list_pages( 'sort_column=menu_order&title_li=&child_of=' . $post->post_parent . '&echo=0' );
    $childpages = wp_list_pages( 'sort_column=menu_order&title_li=&child_of=' . $post->ID . '&echo=0' );

if ( $childpages ) {

    $string = '<ul>' . $childpages . '</ul>';

return $string;


add_shortcode('wpb_childpages', 'wpb_list_child_pages');

That function can be found here: https://www.wpbeginner.com/wp-tutorials/how-to-display-a-list-of-child-pages-for-a-parent-page-in-wordpress/

Thank you.

1 Answer 1


I think the solution that would work out best for you is just using your own query. Since you have modified pages to have categories and tags taxonomies wp_list_pages doesn't support excluding the arguments quite like that. You could get an array of all page IDs that are assigned to each category, but you'd be writing a query for that. You might as well just do it once, and forget about using wp_list_pages.

As far as implementation goes - the code above was giving me errors since $string can end up being undefined and attempts to return that. Here's a working example that I think achieves what you're going after:

// functions.php

// These hooks are only here as I wasn't sure if you actually meant that you were adding cat/tag to pages as there wasn't any code provided referencing that.
add_action( 'init', 'wpse356728_add_tags_cats_pages' );
add_action( 'pre_get_posts', 'wpse356728_tags_cats_update_queries' );

 * Register the tags and cats tax for pages.
function wpse356728_add_tags_cats_pages() {
    register_taxonomy_for_object_type( 'post_tag', 'page' );
    register_taxonomy_for_object_type( 'category', 'page' );  

 * Ensure pages are added to any queries for tags and cats.
function wpse356728_tags_cats_update_queries( $wp_query ) {
    if ( $wp_query->get( 'tag' ) ) {
        $wp_query->set( 'post_type', 'any' );
    if ( $wp_query->get( 'category_name' ) ) {
        $wp_query->set( 'post_type', 'any' );

 * Create list of pages that are parents with optional exclude
 * pages by category name.
 * @param array $cats Array of category names to exclude from query.
function wpse356728_list_child_pages( $cats = [] ) { 
    global $post;

    $current_ID = $post->ID;

    if ( is_page() && $post->post_parent ) {
        $child_of = $post->post_parent;
    } else {
        $child_of = $current_ID;

    // Get the category IDs for passed in category names for query.
    $cats = ( array ) $cats;
    $cats = array_map( 'get_cat_ID', $cats );

    $args = [
        'post_type'      => 'page',
        'posts_per_page' => -1,
        'post_parent'    => $child_of,
        'order'          => 'DESC',
        'orderby'        => 'menu_order',
        'category__not_in' => $cats,

    $parent = new WP_Query( $args );

    if ( $parent->have_posts() ) : ?>
        <?php while ( $parent->have_posts() ) : $parent->the_post(); ?>
                $current = function( $output ) use ( $current_ID ) {
                    return get_the_ID() === $current_ID ? $output : '';
                <li class="page_item page_item-<?php the_ID(); echo $current( ' current_page_item' ); ?>">
                    <a href="<?php the_permalink(); ?>" <?php echo $current( 'aria-current="page"' ); ?>><?php the_title(); ?></a>
        <?php endwhile; ?>
    <?php endif; wp_reset_postdata();

The method wpse356728_list_child_pages is really what you should be looking at. Instead of using wp_list_pages as you had - this shows how to get output from WP_Query that is pretty much identical markup, and allows you to pass in the category names you want to have pages excluded from in the query list returned. This will give more flexibility as theres many other args you can pass to the query.


// page.php
<?php wpse356728_list_child_pages( [ 'sports', 'animals', 'movies' ] ); ?>

The above code would omit sports, animals, and movies categories from the query. You can modify the output of wpse356728_list_child_pages to have it returned as a string and create a shortcode if that's necessary. In my test theme - I just used a hook that existed on page.php and added the code to my functions.php instead like this:

// functions.php

add_action( 'theme_name_after_page_content', function() {
    $cats_to_exclude = [ 'sports', 'animals', 'movies' ];
    wpse356728_list_child_pages( $cats_to_exclude );
} );

  • First of all let me say thank you so much for taking the time to work this out! Secondly let me say sorry for the delay in my response, I thought I was going to get an email when someone responded and I didn't, so I hadn't checked. The only issue I had was minor, and that was that each page that was listed was repeated as a new list instead of just a list item. So I moved the <ul> out of the function in the functions.php file and wrapped it around the function in the page.php file. Again, thank you so much!
    – Phil
    Jan 23, 2020 at 21:45
  • not a problem! Good catch, I wasn't paying attention to that :P I updated the answer so it reflects that change in case anyone else ends up here :)
    – Tim Elsass
    Jan 23, 2020 at 21:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.