I have a section on my site that needs a list of pages that are descendents of a particular ID. I need them listed on the child and grandchildren pages. But the page titles are too long, so I need to use the slugs as the titles. I thought I could just use wp_list_pages and specifically call descendents of this ID BUT there doesn't seem to be a way for wp_list_pages to list the slugs instead of page titles. furthermore, i have to use a str_replace for the dashes in the slugs. can someone tell me where i can even start with this? i ended up using the below, which isn't working any better... any insights?

function is_tree($pid) {      
global $post;         
$anc = get_post_ancestors( $post->ID );

foreach($newanc as $ancestor) 
    if(is_page() && $ancestor == $pid) {
    return true;

return true; // we're at the page or at a sub page else return false; // we're elsewhere };

  • wp_list_pages() is a great function, but it falls down when situations such as your arise because it mearly lists the pages, as opposed to returning the results as a useful Object. You can work around this, but you need to use what is called a 'walker'. This is basically a Class Extender (let me know if you are not sure what that is), so you can overwrite functions used by wp_list_pages() and get your desired output. I have several custom walkers on my site, so I will try and come up with a usable one for you in the next day or so.
    – David Gard
    Commented Nov 14, 2012 at 16:54

2 Answers 2


Ok, this code should set you on the right path. It's not designed to do exactly what you want, but judging by your code above, you seem fairly compitent, so should be able to amend as required. I'd also recommend looking at the Walker_Page Class, that way you can compare the methods below to those definded by Walker_Page, and see what has been changed (wp-includes/post-template.php).

Place this in functions.php, and then when calling wp_list_pages($args), be sure to include the argument 'walker' => new Walker_Custom_Page.

 * Custom Walker for creating the page list.
class Walker_Custom_Page extends Walker_Page {

     * @see Walker::start_lvl()
     * @param string $output Passed by reference. Used to append additional content.
     * @param int $depth Depth of page. Used for padding.
    function start_lvl(&$output, $depth) {
        $indent = str_repeat("\t", $depth);
        $output .= "\n{$indent}<ul class='children'>\n";

     * @see Walker::start_el()
     * @param string $output Passed by reference. Used to append additional content
     * @param object $page Page data object
     * @param int $depth Depth of page. Used for padding
     * @param array $args
     * @param int $current_page Page ID
    function start_el(&$output, $page, $depth, $args, $current_page) {

        /** Add an indent (if required) before the page */
        if($depth) :
            $indent = str_repeat("\t", $depth);
        else :
            $indent = '';

        /** Make 'new_href' and 'image' arrays to avoid errors if it is empty */
        if(!is_array($args['new_href'])) : $args['new_href'] = array($args['new_href']); endif;

        /** Check to see if an alternitive link has been specified and set the $link_href **/
        if(array_key_exists((string)$page->ID, $args['new_href'])) : 
            $link_href = $args['new_href'][(string)$page->ID];
            $target = '';
        elseif(array_key_exists('_'.(string)$page->ID, $args['new_href'])) : 
            $link_href = $args['new_href']['_'.(string)$page->ID];
            $target = ' target="_blank"';
        else :
            $link_href = get_permalink($page->ID);
            $target = '';

        /** Make the link for the page */
        $page_children = get_pages('child_of='.$page->ID);
        $link_in = (in_array($page->ID, $args['no_link'])) ? '<a>' : '<a href="'.$link_href.'"'.$target.'>';
        $link_out = '</a>';
        $link_after = (count($page_children) !== 0 && $depth !== 0) ? ' &raquo;' : false;
        $link = $link_in.$link_before.apply_filters('the_title', $page->post_title, $page->ID).$link_after.$link_out;

        /** Extract the args */
        extract($args, EXTR_SKIP);

        /** Make the CSS class for the link */
        $css_class = array('page_item', 'page-item-'.$page->ID);
        $current_page = apply_filters('change_current_page', $current_page);
        if(!empty($current_page)) :

            $_current_page = get_page($current_page);

            if(isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors)) :
                $css_class[] = 'current_page_ancestor';

            if($page->ID == $current_page) :
                $css_class[] = 'current_page_item';
            elseif($_current_page && $page->ID == $_current_page->post_parent) :
                $css_class[] = 'current_page_parent';

        elseif($page->ID == get_option('page_for_posts')) :
            $css_class[] = 'current_page_parent';

        /** Filter the page CSS class so that extra can be added */
        $css_id = $css_class[1];
        $css_class = implode(' ', apply_filters( 'page_css_class', $css_class, $page, $depth, $args, $current_page));

        /** Add the page link to $output */
        $output .= $indent . '<li id="'.$css_id.'" class="' . $css_class . '">' . $link;

        /** Add the time that the page was modified (if desired) */
        if(!empty($show_date)) :

            if('modified' == $show_date) :
                $time = $page->post_modified;
            else :
                $time = $page->post_date;

            $output .= ' '.mysql2date($date_format, $time);




This is a little more concise:

wp_list_pages( ... );

function wpse72581_use_slug_not_title( $title, $post_id ){
    $post_obj = get_post( $post_id );
    $post_slug = esc_html($post_obj->post_name);
    return $post_slug;

I'm not sure why you would prefer to use the slug though: as you mention this is sanitised by adding dashes and removing accents etc. Perhaps instead it might be better to trim the title? (see wp_trim_words())

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.