When would you use pagination?

This module renders navigation for pagination. Such navigation usually appears as a list of "1, 2, 3, next…" links at the bottom of search results. Here is an example from the Skyscrapers demo site. In ProcessWire, this is most commonly used when you want to separate a large list of links into multiple pages, making it easier for the user to browse, and quicker for the server to render. For instance, lets say that you needed to generate a list of links to 100 pages, but only wanted to display 10 per page. This module makes that easy, and it is already hooked into other modules to make it even simpler. 

Enabling Pagination

  1. Make sure the "Pager" (MarkupPagerNav) module is installed in Admin > Modules. If it's not installed, click the Install link next to it. 
  2. Determine what template(s) you want to use pagination with. Go to Admin > Setup > Templates > [Your Template] > URLs, and check the box for: Allow Page Numbers. Save. 
  3. Pagination should now be enabled and ready to use in your templates. 

Using Pagination in your Templates

The MarkupPagerNav module is designed to automatically paginate the results of any API method that returns more than one page (i.e. a PageArray). The most common example is the $pages->find() method, which we will use in the examples below. 

Example #1

In this example, we will retrieve all pages in the site with an ID greater than 1, limit the results to 10, and sort alphabetically by title. The result would be all pages except for the homepage, retrieving a max of 10 at a time. This is a somewhat contrived example, so you would most likely want to replace the selector in this example with with your own selector. Regardless, you'll always want to specify a "limit" equal to the number of results you want to show per page, when using pagination. 

$results = $pages->find("id>1, limit=10, sort=title");
echo $results->render();        

We used the built-in render() method above to print a simple list of the pages found. Assuming that there are more pages found than what you specified for the "limit", you should see a list of pagination links appear at the end of the list. This works because when pagination is enabled, the render() method automatically prints pagination when it detects that there are more results than what was provided. You might notice that the pagination links are unstyled. There are some CSS styles further in this document that work as a good starting point to paste into your stylesheet. 

Example #2

Most likely you aren't using that built-in render method in the previous example because you are generating your own markup for your search results. Not to worry, generating your pagination is still just as easy. Just call $results->renderPager() rather than $results->render(). That will render just the pagination links, leaving you to generate the markup for your results. In the example below, we use this method, but also print out the pagination at both the top and bottom of the results: 

$results = $pages->find("id>1, limit=10, sort=title");
$pagination = $results->renderPager();
echo $pagination;
echo "<ul>";
foreach($results as $result) {
    echo "<li><a href='{$result->url}'>{$result->title}</a></li>";
echo "</ul>";
echo $pagination;   

Do I have to keep track of the page number?

Not unless you want to. ProcessWire will keep track of the page number and automatically adjust the results returned from any method that returns pages and has a limit greater than 1 applied to it. Generally that means you don't have to pay any attention to what the page number is in your template code, unless you want to print it in a headline or <title> tag, for instance. You can always retrieve the current page number from $input->pageNum. 

Are there any side effects?

If your template has page numbers enabled (as this page instructed) then ProcessWire is going to paginate all calls to API functions that return PageArrays where you have specified a limit greater than 1. This includes:

// where "n" is any number greater than 1

In my experience, the situations where this would be an undesirable side effect are rare. However, if you are viewing a paginated list on any page number greater than 1, and you find ProcessWire returning the wrong set of pages, then replace your "limit=n" selector with the following:

$pages->find("start=0, limit=n"); 

That will ensure that ProcessWire does not attempt to paginate the results of that function call when on page numbers greater than 1. 

Is Pagination Compatible with Template Caching?

Yes, pagination may be used with template caching and ProcessWire will cache up to 999 paginated versions of each page. 

Note that GET and POST vars are not cached (regardless of whether you are using pagination), so if you are paginating results generated from values in GET or POST vars, you should not use template caching. 

Styling Pagination

If you tried out the examples above, you would have noticed that the pagination was unstyled and thus appeared just like any other unordered list on your site. ProcessWire doesn't make any assumptions about your site's design, so you should style the list in your CSS stylesheet(s) to be consistent with your site. But here are some styles to paste into your stylesheet as a good starting point, or feel free to use them as-is if you prefer. 

.MarkupPagerNav {
        clear: both;
        margin: 1em 0;
        font-family: Arial, sans-serif;
.MarkupPagerNav li {
	display: inline;
	list-style: none;
	margin: 0;

.MarkupPagerNav li a,
.MarkupPagerNav li.MarkupPagerNavSeparator {
	display: block;
	float: left;
	padding: 2px 9px;
	color: #fff;
	background: #2f4248;
	margin-right: 3px;
	font-size: 10px;
	font-weight: bold;
	text-transform: uppercase;

.MarkupPagerNav li.MarkupPagerNavOn a,
.MarkupPagerNav li a:hover {
	color: #fff;
	background: #db1174;
	text-decoration: none;

.MarkupPagerNav li.MarkupPagerNavSeparator {
	display: inline;
	color: #777;
	background: #d2e4ea;
	padding-left: 3px;
	padding-right: 3px;

Custom Options and Markup (Optional)

The examples above have ProcessWire generating the HTML5/XHTML markup for your pagination links, and all use the default pagination options. These are typically just fine, but there may be an instance where you want to modify this behavior. To do so, specify an $options array to the renderPager method. You may specify one or more options. The defaults will be used for any options that you omit. Below is an example that might modify the markup output by the renderPager method (though the values shown here are actually the defaults): 

echo $results->renderPager(array(
    'nextItemLabel' => "Next",
    'previousItemLabel' => "Prev",
    'listMarkup' => "<ul class='MarkupPagerNav'>{out}</ul>",
    'itemMarkup' => "<li class='{class}'>{out}</li>",
    'linkMarkup' => "<a href='{url}'><span>{out}</span></a>"

Below is a reference of all the options available, along with the defaults. 

Custom Options Reference

numPageLinksNumber of links (integer) that the pagination navigation should have. Default: 10
getVarsArray of GET vars that should appear in the pagination links, or leave empty and populate $input->whitelist (preferred). This is handy with search results that may include custom options that should remain on each paginated page (like sorting order, fields to search, etc.). It is recommended that you populate such GET vars with $input->whitelist($name, $value) rather than specifying them here as an option to MarkupPagerNav. Default: empty 
baseUrlThe baseUrl (string) from which the navigiation item links will start. You should omit this unless you are rending pagination results to some other page. Default: blank
listMarkupThe markup (string) used to generate the list container. Place "{out}" where the list items will appear. Default: <ul class='MarkupPagerNav'>{out}</ul> 
itemMarkupThe markup (string) used to generate each pagination list item. "{class}" and "{out}" must be specified, as in the default. Default: <li class='{class}'>{out}</li> 
linkMarkupThe markup (string) used to generate each pagination link. "{url}" and "{out}" must be specified, as in the default. Default: <a href='{url}'><span>{out}</span></a> 
nextItemLabelThe label (string) used for the "next" button. Default: Next 
previousItemLabelThe label (string) used for the "previous" button. Default: Prev 
separatorItemLabelThe label (string) used for the list "separator". Default: … 
separatorItemClassClass attribute (string) for the "separator" list item. Default: MarkupPagerNavSeparator 
nextItemClassClass attribute (string) used for the "next" button list item. Default: MarkupPagerNavNext 
previousItemClassClass attribute (string) used for the "previous" button list item. Default: MarkupPagerNavPrevious 
lastItemClassClass attribute (string) used for the "last" button list item (represented by the last page number). Default: MarkupPagerNavLast 
currentItemClassClass attribute (string) used for the "current/active/on" button list item (the page number being viewed). Default: MarkupPagerNavOn 

Note: if your copy of ProcessWire was downloaded before January 4, 2011, please download the latest version for consistency with the options listed above. 


  • Thomas

    Thomas 4 years ago 63

    I'm missing $results->getTotal();. It took me hours to get this right. count($results) only returns the limit set in your search, getTotal() returns the number of the real search results.

  • Gabi Tenita

    Gabi Tenita 4 years ago 54

    There are also the following options:

    firstItemClass ('MarkupPagerNavFirst')

    firstNumberItemClass ('MarkupPagerNavFirstNum')

    lastNumberItemClass ('MarkupPagerNavLastNum')

  • daslicht

    daslicht 3 years ago 55

    I get the Pager rendered but when I click its links the page just refreshes ?

    Do I have to implement the Page show logic myself?

  • Sebastian

    Sebastian 3 years ago 45

    Is there any way to translate the URL part "pageX"?

    • tpr

      tpr 2 years ago 56

      If you're looking for how to change "page2", you can set it in config.php $config->pageNumUrlPrefix:

  • ben

    ben 3 years ago 53

    not working, /?page=2 doesnt seem to auto populate the results, so ive had to hack into it instead. how does this work? (just shows the same results each time instead of going to the next items in the list)

  • ryan

    ryan 3 years ago 53

    Ben: see step #2 under the section "Enabling Pagination" above. If you are seeing a URL like ?page=2 that indicates that pagination is not enabled for the template, and you would need to enable it.

  • ryan

    ryan 3 years ago 45

    Sebastian: You can translate it if you are using the LanguageSupportPageNames module (included with the core). The pagination URL format is definable per language in that module's settings.

  • Can

    Can 3 years ago 83

    Needed to change current link markup..found more options in MarkupPagerNav.module like

    'currentLinkMarkup' => "{out}"

    maybe someone else needs it as well ;-)


  • obiuno

    obiuno 3 years ago 75

    is there any way to change the output so that I could use it with let say infinite scroll jquery plugin???

  • tpr

    tpr 8 months ago 10

    Since 3.0.?? the default empty setting ('') for getVars causes a PHP error. Use an empty array instead (array()).

  • Ferdi Derksen

    Ferdi Derksen 3 months ago 00

    When using a multi-instance setup, you should at least set the option 'page' with the $page object where you are rendering the pagination. Mostly you will also set the option 'baseUrl' to the current URL > "$page->httpUrl()" .

  • Fred Vaughn

    Fred Vaughn 4 weeks ago 00

    Is there a solid way to figure out how many pages are returned? Example: A search results page returned 10 pages. How can I extract '10' so I can write code to figure out if I'm on the last page or not?

Post a Comment

Your e-mail is kept confidential and not included with your comment. Website is optional.