How do I get pagination to work for get_posts() in WordPress?

I’m working on a WordPress site and I’ve created a page template that displays posts by a category slug. To do this, I create a field for the page, WP_Catid, and set it equal to the category slug I want to display posts from. However, I only want five posts to show up per page with pagination links at the bottom of those posts. How do I get the pagination links to display properly?

My code is as follows:

<div id="container">
  <div id="content" role="main">
    <?php
      $btpgid=get_queried_object_id();
      $btmetanm=get_post_meta( $btpgid, 'WP_Catid','true' );
      $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;

      $args = array( 'posts_per_page' => 5,
                     'category_name' => $btmetanm,
                     'paged' => $paged,
                     'post_type' => 'post' );

      $myposts = get_posts( $args );
      foreach ( $myposts as $post ) : setup_postdata( $post ); 
        echo "<div style='border:2px groove black; margin-bottom:5px;'><h3 class='btposth'>";
        the_title(); 
        echo "</h3><div class='btpostdiv'>";
        the_content();
        echo "</div></div>";
      endforeach; 
      next_posts_link( 'Older Entries'); //not displaying
      previous_posts_link('Newer Entries &raquo;'); //not displaying
      wp_reset_postdata();
    ?>
  </div><!-- #content -->
</div><!-- #container -->

Related posts

Leave a Reply

5 comments

  1. The sweet and short of this, don’t use get_posts if you need paginated queries. get_posts works perfectly if you are going to use a custom query that doesn’t need pagination, but it really becomes a big complicated mess when you need to introduce pagination.

    I think the easiest and most appropriate here is to make use of WP_Query to construct your custom query, that is, if you can’t use pre_get_posts to alter the main query to get your desired output from the main query.

    I do think that next_posts_link() and previous_posts_link() is better to use with a custom query, ie with WP_Query. You must just remember however to set the $max_pages parameter when you make use of a custom query, otherwise your pagination will break

    With a few minor tweaks, your query should look like this

    <div id="container">
    <div id="content" role="main">
    <?php
    $btpgid=get_queried_object_id();
    $btmetanm=get_post_meta( $btpgid, 'WP_Catid','true' );
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    
    $args = array( 'posts_per_page' => 5, 'category_name' => $btmetanm,
    'paged' => $paged,'post_type' => 'post' );
        $postslist = new WP_Query( $args );
    
        if ( $postslist->have_posts() ) :
            while ( $postslist->have_posts() ) : $postslist->the_post(); 
    
    
                 echo "<div style='border:2px groove black; margin-bottom:5px;'><h3 class='btposth'>";
                     the_title(); 
                 echo "</h3><div class='btpostdiv'>";
                     the_content();
                 echo "</div></div>";
    
             endwhile;  
    
                 next_posts_link( 'Older Entries', $postslist->max_num_pages );
                 previous_posts_link( 'Next Entries &raquo;' ); 
            wp_reset_postdata();
        endif;
        ?>
    
    </div><!-- #content -->
    </div><!-- #container -->
    
  2. Pieter Goosen’s answer is completely correct, and his suggestion to use WP_Query instead makes the most sense. However, I stumbled across this question whilst looking for pagination with get_posts outside of the loop, so I thought this might also be a useful alternative for anyone else:

    get_posts has a direct property called offset which achieves pretty much the same thing as paged in WP_Query; but where paged refers to pagination (e.g. 1, 2, 3), offset is the actual number of posts you want to offset your query by (e.g. 5, 10, 15). With a little maths – numberToShow * pageNumber – you can get the correct offset easily:

    $paged = (get_query_var('paged')) ? get_query_var('paged') : 0;
    
    $postsPerPage = 5;
    $postOffset = $paged * $postsPerPage;
    
    $args = array(
        'posts_per_page'  => $postsPerPage,
        'category_name'   => $btmetanm,
        'offset'          => $postOffset,
        'post_type'       => 'post'
    );
    
    $myposts = get_posts($args);
    

    The initial paged value in this example is 0 rather than 1 because, when multiplying the posts_per_page, you want the initial offset to be 0 rather than 5.

    This can be most handy if you want a little more granular control rather than straightforward pagination, but should work just as well in combination with the loop in the accepted answer.

  3. Try to change your $args:

    $args = array( 
    'posts_per_page' => 5,
    'category_name' => $btmetanm,
    'post_type' => 'post',
    'paged' => ( get_query_var('paged') ? get_query_var('paged') : 1 )
       );
    

    And just after loop put this:

    if (function_exists('wp_pagenavi')) {
    wp_pagenavi();
    }
    
  4. I will not tell you that using get_posts() is the right thing to do…but here is some basic pagination code I setup using get_posts().

    EDIT: As Pieter pointed out, this isn’t meant to be used in production code. But just something I was playing around with anyway to see if I could get pagination working with get_posts(). If you are in a production environment you wouldn’t want to use this.

    $cpt_name = 'post-type'; //add your own post type
    
    //what pagination page are we on?
    if(! empty($_GET['pag']) && is_numeric($_GET['pag']) ){
        $paged = $_GET['pag'];
    }else{
        $paged = 1;
    }
    //you could use this if you want, just make sure to use "/page/2" instead of "?pag=2" in the pagination links.
    //$paged = (get_query_var('paged')) ? get_query_var('paged') : 0;
    
    //how many posts should we display?
    $posts_per_page = (get_option('posts_per_page')) ? get_option('posts_per_page') : 10; 
    
    //let's first get ALL of the possible posts
    $args = array(
            'posts_per_page'   => -1,
            'orderby'          => 'title',
            'order'            => 'ASC',
            'fields'           => 'ids',
            'post_type'        => $cpt_name
        );
    
    $all_posts = get_posts($args);
    
    //how many total posts are there?
    $post_count = count($all_posts);
    
    //how many pages do we need to display all those posts?
    $num_pages = ceil($post_count / $posts_per_page);
    
    //let's make sure we don't have a page number that is higher than we have posts for
    if($paged > $num_pages || $paged < 1){
        $paged = $num_pages;
    }
    
    //now we get the posts we want to display
    $args = array(
            'posts_per_page'   => $posts_per_page,
            'orderby'          => 'title',
            'order'            => 'ASC',
            'post_type'        => $cpt_name,
            'paged'        => $paged
        );
    
    $my_posts = get_posts($args);
    
    //did we find any?
    if(! empty($my_posts)){
    
        echo '<div id="my-posts-wrapper">';
    
        //THE FAKE LOOP
        foreach($my_posts as $key => $my_post){
                    //do stuff with your posts
            echo '<div class="my-post">'.$my_post->post_title.'</div>';
    
        }
    
        echo '</div>';
    
        //we need to display some pagination if there are more total posts than the posts displayed per page
        if($post_count > $posts_per_page ){
    
            echo '<div class="pagination">
                    <ul>';
    
            if($paged > 1){
                echo '<li><a class="first" href="?pag=1">&laquo;</a></li>';
            }else{
                echo '<li><span class="first">&laquo;</span></li>';
            }
    
            for($p = 1; $p <= $num_pages; $p++){
                if ($paged == $p) {
                    echo '<li><span class="current">'.$p.'</span></li>';
                }else{
                    echo '<li><a href="?pag='.$p.'">'.$p.'</a></li>';
                }
            }
    
            if($paged < $num_pages){
                echo '<li><a class="last" href="?pag='.$num_pages.'">&raquo;</a></li>';
            }else{
                echo '<li><span class="last">&raquo;</span></li>';
            }
    
            echo '</ul></div>';
        }
    }
    

    I hope someone gets some use out of this 🙂

    EDIT: What the heck! Going to do something the wrong way…might as well do it right! Here is some LESS as well (without any mixins).

    .pagination             { margin: 30px 0px;
        ul                    { display:block; list-style-type:none; margin:0 auto; padding: 0px; 
          li                  { display:inline-block; list-style-type:none; margin:0; padding:0;
            a, span           { display:inline-block; font-size: 14px; width:auto; min-width:26px; height:26px; line-height: 26px; border: 1px solid #dddddd; border-right: 0px; background:#FFFFFF; color:#FF0000; padding: 5px; text-align: center;
              &:hover         { cursor:pointer; text-decoration:none; }
    
              &.first         { border-top-left-radius: 3px; border-bottom-left-radius: 3px; }
              &.last          { border-top-right-radius: 3px; border-bottom-right-radius: 3px;}
            }
    
            span.last,
            span.first        { color: #FF0000;
                &:hover       { cursor: default; }
            }
    
            a.last,
            a.first           { 
                &:hover       {  }
            }
    
            a:hover, 
            &.active a, 
            .current          { background:#FF0000; color:#ffffff; border-color: #b21712; }
    
            &:last-child      { 
                a, span       { border-right: 1px solid #dddddd; }
    
                a             { 
                    &:hover   { border-color: #FF0000; }
                }
            }
          }
        }
    }
    
  5. WordPress Pagination Code for Posts (NEW)

    Copy and paste this piece of code and enjoy that. 🙂

    <?php
     
    $currentPage = get_query_var('paged');
     
     
    // General arguments
     
    $posts = new WP_Query(array(
        'post_type' => 'post', // Default or custom post type
        'posts_per_page' => 10, // Max number of posts per page
        //'category_name' => 'My category', // Your category (optional)
        'paged' => $currentPage
    ));
     
     
    // Top pagination (pagination arguments)
     
    echo "<div class='page-nav-container'>" . paginate_links(array(
        'total' => $posts->max_num_pages,
        'prev_text' => __('<'),
        'next_text' => __('>')
    )) . "</div>";
     
     
    // Content display
     
    if ($posts->have_posts()) :
        while ($posts->have_posts()) :
            $posts->the_post();
            echo "<div class='post-wrap'>";
            the_title();
            the_content();
            echo "</div>";
        endwhile;
    endif;
     
     
    // Bottom pagination (pagination arguments)
     
    echo "<div class='page-nav-container'>" . paginate_links(array(
        'total' => $posts->max_num_pages,
        'prev_text' => __('<'),
        'next_text' => __('>')
    )) . "</div>";
     
    ?>