Paginating an Advanced Custom Fields Repeater

Hands down my favourite WordPress plugin is Elliot Condon’s Advanced Custom Fields, and it’s made even more powerful by the Repeater Field add-on. But a Repeater can get unwieldy when it contains a large number of items, and you might find yourself wanting to paginate the results when you display them to the user. Here’s a technique for doing that.

In this example we will paginate a Repeater image gallery mapped to a custom field named image_gallery with a sub field named image which contains an image object. Our Repeater will be displayed on a page at the URL /gallery.

10 images will be displayed per page, and pagination links will allow the user to navigate between the gallery’s pages at pretty URLS such as /gallery/2/, /gallery/3/ and so on.

If you’re wondering what special magic you need to perform to get those pretty permalinks working, the answer is… none! WordPress automagically converts URL segments such as /2/ into a query variable named "page". Very handy!

In your page template:

<?php
/* 
 * Paginate Advanced Custom Field repeater
 */

if( get_query_var('page') ) {
  $page = get_query_var( 'page' );
} else {
  $page = 1;
}

// Variables
$row              = 0;
$images_per_page  = 10; // How many images to display on each page
$images           = get_field( 'image_gallery' );
$total            = count( $images );
$pages            = ceil( $total / $images_per_page );
$min              = ( ( $page * $images_per_page ) - $images_per_page ) + 1;
$max              = ( $min + $images_per_page ) - 1;

// ACF Loop
if( have_rows( 'image_gallery' ) ) : ?>

  <?php while( have_rows( 'image_gallery' ) ): the_row();

    $row++;

    // Ignore this image if $row is lower than $min
    if($row < $min) { continue; }

    // Stop loop completely if $row is higher than $max
    if($row > $max) { break; } ?>                     
    
    <?php $img_obj = get_sub_field( 'image' ); ?>
    <a href="<?php echo $img_obj['sizes']['large']; ?>">
      <img src="<?php echo $img_obj['sizes']['thumbnail']; ?>" alt="">
    </a>

  <?php endwhile;

  // Pagination
  echo paginate_links( array(
    'base' => get_permalink() . '%#%' . '/',
    'format' => '?page=%#%',
    'current' => $page,
    'total' => $pages
  ) );
  ?>

<?php else: ?>

  No images found

<?php endif; ?>

A note about Custom Post Types

This technique will also work on Custom Post Type single templates. Your pagination permalinks will have the format /post-type/post-slug/2/.

Credits

My solution was inspired by Elliot Condon and Twansparent’s contributions to the Advanced Custom Fields forum.

33 thoughts on “Paginating an Advanced Custom Fields Repeater

  1. Nils says:

    Hi, I found your post through the ACF post and I like your solution the best mainly because it is less code, but the pagination doesn’t seem to be working.

    I see “1 2 Next »” and it says the URL for 2 is “domain.com/portfolio/post-name/page/2/”, but when I click on it, it reloads the parent page(“domain.com/portfolio/post-name/”).

    What should I do?

    (PS: the website is not open to public yet so I cant share the webpage)

  2. Andrea Moro says:

    Hey Jonathan,

    I arrived to your blog reading a comment about the pagination on Elliot’s blog. Your solutions is much clean and leverage WP functionalities which is much better.

    However, there is a bug. Your code doesn’t take in account the next page and keep loading records from number 1, thus making the pagination exercise useless.

    I’m not sure whether this is the one above is the same code you run on your site though.

    Best
    Andrea

  3. Jonathan says:

    @Nils Sorry, I should have mentioned in my post that the technique as I originally described only worked on standard WordPress pages, not within a Custom Post Type. I’ve updated the post with a technique that should work with pages or with single custom post types. I have not tested this with standard posts.

    Please note that the paginated permalinks have changed, and no longer include a /page/ segment. Permalinks now look like this:

    /your-page-slug/2/

    or for custom post types:

    /post-type/post-slug/2/

    The relevant code changes are:

    The query variable referenced at the start of the code snippet becomes ‘page’ instead of ‘paged’:

    if( get_query_var('page') ) {
    $page = get_query_var( 'page' );
    } else {
    $page = 1;
    }

    And the base and format options passed to paginate_links become:

    'base' => get_permalink() . '%#%' . '/',
    'format' => '?page=%#%',

    Let me know how you get on with this modified permalink structure.

  4. Jonathan says:

    @Andrea See my reply to Nils above. It probably applies in your situation too.

  5. Nils says:

    @Jonathan
    It works great now! Thanks and I love your site here :]

  6. Oleg says:

    Hi, thanks for that,
    how is it possible to use the pagination links only with previous and next buttons in this case?
    Thanks

  7. andi says:

    Hi Jonathan,
    thank you for the code, this works very well!

    regards,
    andi

  8. Jonathan says:

    @Oleg Probably the simplest solution is to use CSS to hide the number buttons, leaving only the prev and next buttons:

    .page-numbers:not(.prev):not(.next) {
    display: none;
    }

  9. oleg says:

    @jonathan
    it works – thanks

  10. Amir says:

    Hey Jonathan,

    I’ve just found your blog via Advanced custom fields forum, Thanks for the developing the code, it is super useful.
    I was thinking about having a “load more” button, With your code we’re almost there, do you have any thought to implement that?

  11. Jonathan says:

    @Amir Have a look at Paul Irish’s infinite scroll jQuery plugin https://github.com/paulirish/infinite-scroll

    Infinite Scroll can be configured to work with a “load more” button. http://stackoverflow.com/questions/8770205/is-there-an-infinite-scroll-plugin-with-a-load-more-button

  12. Erez says:

    It works great! – Thank you very much Jonathan!

  13. arpita says:

    Hello,

    I want a next, prev arrow functionality for testimonials.. means I want display one testimonial and other is next page..and previous page also.

    I want a solution with ACF custom field plugin suitable code..If any one has code then plzzz help me quickly.

    It will appreciated..

    Thank You in advance..

  14. Jonathan says:

    @arpita See my reply to @oleg above.

  15. arpita says:

    Hello Jonathan,

    Ya implement first that tutorial but its not works for me..

    How to I link next prev button using testimonial.. display only one testimonial at a time.

    Help me..

    Thank you

  16. Kassandra says:

    Works wonderfully. Thanks a lot for sharing!

  17. Alex says:

    Can say i liked your solution, cleaner.. but your solution get me from page 2 to page 1 and leaves /1/ in link.. it will kinda look like duplicated content..

  18. Tom Cafferkey says:

    Hi Jonathan,

    Brilliant post. Worked perfectly.

    Cheers.

  19. Paudie says:

    Jonathan,
    That worked a treat for me, thank you.

    Paudie

  20. Mark says:

    This works brilliantly. Thanks for sharing!

    Is there a way to reverse the order of the rows, so that the most recently added repeater item would appear on the first page?

  21. Jonathan says:

    @Mark You could use this technique described in ACF’s documentation.

  22. Cat Matteson says:

    Hi, any idea on how to get this method to work with ACF Image Gallery?

  23. Mark says:

    This is awesome. The only thing I can’t figure out is why the paginate_links() doesn’t display. The only difference I can see that might be the culprit is I’m displaying a repeater on a page from an ACF Theme Options section.

    Here’s a link to see my code: http://www.codeshare.io/r1Hvp

    Would love to hear your thoughts.

    Cheers,

  24. dev says:

    That worked a treat for me, thank you.

  25. Alex says:

    Awesome post man, thank you!

  26. Matthew Pont says:

    This is amazing – literally just worked perfectly. I had no idea how I was going to paginate my ACF repeater, so Googled it and this came up. Saved me a few hours work at least. Thanks!

  27. ingvi says:

    Nice solution, so simple. Thanks for sharing. I guess after setting up a pagination it would be possible to send an ajax call. I am though not sure how to proceed. Do you/someone have any thoughts on that?

  28. Roy Moses says:

    I like this solution to use and how it looks on the wordpress end and the user end, but unfortunately I can’t use it nor I would recommend anyone else of using it. this method is not passing through WordPress system such as in using tag.

    by using this method WordPress has no knowledge of your paginated content
    and it does not give a different Canonical tag for each page (for example if you are using some SEO plugin such as yoast).

    as a result from all that, all numbered pages are has the same Canonical tag of the main first page which also get the address /1/ and thus duplicating the content on 2 different pages.

    so, if I use this method all of the pages I create will be duplicated once and all numbers pages will canonical to them and will not be indexed in google or any other major search engine.

    I wish I could find a way around it…. been looking for a long time but still can’t find any way to do this, if there is any way you can help me I would be very thankful, thanks for the post tough (:

  29. Hindujhan says:

    Hi i looked at your article it works perfectly,

    Thanks

  30. Arunanithi says:

    Simply Superb. Hats off.

  31. Frederick says:

    Hi. Your code is great, but the flaw Andrea spoke of above still occurs. It does not update the list but continually repeats pulling in rows. Your solution to Nils problem does not solve it, Johnathan. It needs to update the parameters for conditionals involving the row and max. Also, you cannot go back to first link as well. Do you have any solutions to these problems?

  32. Jonathan says:

    @Frederick I believe the problems Andrea and Nils identified were fixed in 2014 and no-one else has identified the same problem since then, so perhaps there is another factor at play which is stopping the code from working in your case? I am using this code on a production site to paginate an ACF repeater without the problems you have described.

  33. Frederick says:

    @Jonathan May please see the link then so that I can see a live example of what it should be like then?

Comments are closed.