I need to get all sub-posts of a specific (root) parent id.

get_posts( array( 'numberposts' => -1, 'post_status' => 'publish', 'post_type' => 'microsite', 'post_parent' => $root_parent_id, 'suppress_filters' => false ) );

WP-Codex: get_post() function has the post_parent but no child_of parameter.

The advantage of the function get_pages() in combination with the child_of paramenter is "... Note that the child_of parameter will also fetch "grandchildren" of the given ID, not just direct descendants."*

5 Answers 5


You will need to loop over those posts and then do more queries for each post, repeating until you find no posts in a query.


function get_posts_children($parent_id){
    $children = array();
    // grab the posts children
    $posts = get_posts( array( 'numberposts' => -1, 'post_status' => 'publish', 'post_type' => 'microsite', 'post_parent' => $parent_id, 'suppress_filters' => false ));
    // now grab the grand children
    foreach( $posts as $child ){
        // recursion!! hurrah
        $gchildren = get_posts_children($child->ID);
        // merge the grand children into the children array
        if( !empty($gchildren) ) {
            $children = array_merge($children, $gchildren);
    // merge in the direct descendants we found earlier
    $children = array_merge($children,$posts);
    return $children;

// example of using above, lets call it and print out the results
$descendants = get_posts_children($post->ID);
echo '<pre>';
echo '</pre>';

Yes the above function calls itself, it's a recursive function. It will keep calling itself until it reaches down to a point where the post being looked at has no children, then it will return without calling itself, and the whole stack will bubble back up building the array of children. You would do good to do further research in this area.

Note that there is an inherent cost to what you want, regardless of wether you use recursive functions or not, that is tied to how many levels of posts you have. 5 levels of posts will be costlier than 2, and it is not a linear scaling. You may want to use transients to cache your output depending on how you do this.

Another way of reducing the cost is by only looking down the tree of posts a certain number of levels, e.g. grandchildren but no great grandchildren. This can be done by passing in a depth parameter, and decrementing it on each recursive call, making sure to return an empty array at the start if the depth is 0 or lower. Many tutorials on recursive functions use this as an example.

  • Problem with this code is that it doesnt give you correct ordering; I get all the top level first, then all the second level after in one flattened array. Any clue on how to fix? Apr 8, 2015 at 21:23
  • This code wasn't intended to do that, you have a different problem that requires a slightly different solution, and a hefty dose of context
    – Tom J Nowell
    Apr 9, 2015 at 0:38

I had a similar requirement but needed to preserve the hierarchy so Tom's answer gave me a running start:

function get_post_offspring($post)
    // Function to programmatically walk down a a post's descendants

    // Get immediate children of current post 
    // and added to a new object element called children
    $post->children = get_children(array("post_parent" => $post->ID, "post_type" => "page", "post_status" => "publish"));
    // if post does not have any children just return the post with
    // with children being an empty array
    if (empty($post->children)) {
        return $post;
    foreach ($post->children as $child) {
        // Foreach child of this post....get the children
        $children = get_children(array("post_parent" => $child->ID, "post_type" => "page", "post_status" => "publish"));
        if (!empty($children)) {
            // if this post has children...then call this function again
            // to assign the children el to this post and walk further down
            // This post's offspring

            $child = get_post_offspring($child);

    return $post;

Now I also needed to traverse through the list with a preserving the order and level.

function render_list_items($children)
    foreach ($children as $child) {
        $has_children = !empty($child->children);

        echo "<li>";
        echo "<a href='" . get_permalink($child) . "'>" . $child->post_title . "</a";
        if ($has_children) {
            // If post has children then create a new <ul> inside current
            // <li> and call function again
            echo "<ul>";
            echo render_list_items($child->children);
            echo "</ul>";

        echo "</li>";


So to put it all together I have something like:

global $post;

$post_id = $post->ID;

// Get ancestors of current post where the last el of array
// is the topmost parent
$ancestors = get_post_ancestors($post->ID);

$root_id = array_pop($ancestors);
// We now have the root post of this post and descend to grab all its offspring
$root_post = get_post($root_id);

$root_children = get_post_offspring($root_post);

    <?php render_list_items($root_children->children); ?>
  • echo "<a href='" . get_permalink($child) . "'>" . $child->post_title . "</a"; missing the closing > on the ahref But brilliant answer otherwise, thanks! Sep 25, 2022 at 16:29

Simply use get_page_children(). It works for every post type (not only pages) and is basically what @TomJNowell showed in the other question, but already implemented by core.

$children = get_page_children( $post->ID, $GLOBALS['wp_query'] );

Above sample is like in Codex. That's why you can simply take the global query object (or any other query object) to be used as search base.

  • But how do you use this for a different post type? Not getting it to work. Apr 8, 2015 at 21:18

Use next shortcode to display all children and grandchildren in hierarchical view. Usage: [my_children_list] or [my_children_list page_id=123]

function my_children_list_func($atts, $content = null) {
    global $post;

    $a = shortcode_atts( array(
            'page_id' => ''
    ), $atts );

    $args = array( 
            'numberposts' => -1, 
            'post_status' => 'publish', 
            'post_type' => 'microsite', 
            'post_parent' => (isset($a['page_id']) && $a['page_id']) ? $a['page_id'] : $post->ID,
            'suppress_filters' => false 

    $parent = new WP_Query( $args );


    if ( $parent->have_posts() ) :?>
            <?php while ( $parent->have_posts() ) : $parent->the_post(); ?>
                    <li><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a>
                    <?php echo do_shortcode('[tadam_children_list page_id='.get_the_ID().']') ?>
            <?php endwhile;?>
    <?php endif; wp_reset_postdata();

    return ob_get_clean();
add_shortcode( 'my_children_list', 'my_children_list_func' );

Just in case someone stumbles upon this and decides to implement the chosen answer, get_pages() works with pages and hierarchical post types. Therefore, you can just use get_pages() with the child_of parameter.

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.