500,000+
August 10, 2026
January 3, 2006
Want to replace the old ← Older posts | Newer posts → links with some page links?
This plugin provides the wp_pagenavi() template tag which generates fancy pagination links.
Much of the 2.x line was the work of scribu, whose write-ups on using it with secondary queries are still the ones the FAQ below points at. The plugin icon is by SimpleIcon from Flaticon.
I spent most of my free time creating, updating, maintaining and supporting these plugins, if you really love my plugins and could spare me a couple of bucks, I will really appreciate it. If not feel free to use it without any obligations.
In your theme, you need to find calls to next_posts_link() and previous_posts_link() and replace them.
In the Twentyten theme, it looks like this:
<div class="nav-previous"><?php next_posts_link( __( '<span class="meta-nav">←</span> Older posts', 'twentyten' ) ); ?></div>
<div class="nav-next"><?php previous_posts_link( __( 'Newer posts <span class="meta-nav">→</span>', 'twentyten' ) ); ?></div>
You would replace those two lines with this:
<?php wp_pagenavi(); ?>
For multipart pages, you would look for code like this:
<?php wp_link_pages( ... ); ?>
and replace it with this:
<?php wp_pagenavi( array( 'type' => 'multipart' ) ); ?>
Go to WP-Admin -> Settings -> PageNavi for configuration.
If you need to configure the CSS style of WP-PageNavi, you can copy the css/wp-pagenavi.css file from the plugin directory into your theme’s directory as wp-pagenavi.css and make your modifications there. This way, you won’t lose your changes when you update the plugin.
The stylesheet sets no font and no text colour, so it inherits both from your theme, and its two border colours are CSS custom properties. A small change needs no copy of the file at all:
.wp-pagenavi {
--wp-pagenavi-border-color: #d0d0d0;
--wp-pagenavi-border-color-current: rebeccapurple;
}
Alternatively, you can set “Use wp-pagenavi.css” to No on the settings page and add the styles to your theme’s style.css file directly.
There are filters that can be used to change the default class names that are assigned to page navigation elements.
wp_pagenavi_class_pageswp_pagenavi_class_firstwp_pagenavi_class_previouspostslinkwp_pagenavi_class_extendwp_pagenavi_class_smallerwp_pagenavi_class_pagewp_pagenavi_class_currentwp_pagenavi_class_largerwp_pagenavi_class_nextpostslinkwp_pagenavi_class_last// Simple Usage - 1 callback per filter
add_filter('wp_pagenavi_class_previouspostslink', 'theme_pagination_previouspostslink_class');
add_filter('wp_pagenavi_class_nextpostslink', 'theme_pagination_nextpostslink_class');
add_filter('wp_pagenavi_class_page', 'theme_pagination_page_class');
function theme_pagination_previouspostslink_class($class_name) {
return 'pagination__control-link pagination__control-link--previous';
}
function theme_pagination_nextpostslink_class($class_name) {
return 'pagination__control-link pagination__control-link--next';
}
function theme_pagination_page_class($class_name) {
return 'pagination__current-page';
}
// More Concise Usage - 1 callback for all filters
add_filter('wp_pagenavi_class_previouspostslink', 'theme_pagination_class');
add_filter('wp_pagenavi_class_nextpostslink', 'theme_pagination_class');
add_filter('wp_pagenavi_class_page', 'theme_pagination_class');
function theme_pagination_class($class_name) {
switch($class_name) {
case 'previouspostslink':
$class_name = 'pagination__control-link pagination__control-link--previous';
break;
case 'nextpostslink':
$class_name = 'pagination__control-link pagination__control-link--next';
break;
case 'page':
$class_name = 'pagination__current'
break;
}
return $class_name;
}
next_posts_link() and previous_posts_link() with wp_pagenavi(). Usage below shows what that looks like in a stock theme.WP-Admin -> Settings -> PageNavi to change the wording and the number of page links.You’re using query_posts() wrong. See The Right Way To use query_posts()
Yes; read this tutorial
If you are running a multi-language plugin, you will probably want to ignore the strings in the options page.
You can do that like so:
<?php wp_pagenavi( array( 'options' => WP_PageNavi_Options::get_defaults() ) ); ?>
Before 3.0.0 this was written as PageNavi_Core::$options->get_defaults(). That property was backed by the bundled SCB framework, which 3.0.0 removes, so the old form no longer works and must be updated.
Yes. The navigation text settings accept the inline SVG elements as well as everything wp_kses_post() allows, so an icon typed into the settings screen survives the save. That allow-list is the wp_pagenavi_allowed_html filter if you need to widen or narrow it.
wp_pagenavi_options instead of pagenavi_options. The old row is copied over and removed automaticallyoptions-general.php?page=pagenavi to options-general.php?page=wp-pagenavipagenavi-css.css is now css/wp-pagenavi.css, and a copy in your theme directory must be renamed to wp-pagenavi.css to keep overriding itPageNavi_Options, PageNavi_Call and PageNavi_Core are WP_PageNavi_Options, WP_PageNavi_Call and WP_PageNavi_Core, and PageNavi_Admin is now WP_PageNavi_SettingsPageNavi_Core::$options has been removed. Use WP_PageNavi_Options::get_defaults() and WP_PageNavi_Options::get() instead. See the FAQPageNavi_Options_Page has been removed and replaced by WP_PageNavi_Settingsscb* classes it defined, since they were loaded into WordPress by whichever plugin bundled it. If your theme or another plugin called html(), html_link(), set_post_field(), scb_init(), scb_register_table(), scb_install_table(), scb_uninstall_table(), scb_admin_notice(), scb_get_query_flags(), scb_list_fold() or scb_list_group_by(), it must now provide them itself. Note that another installed plugin may still be supplying them, so the breakage will only appear once nothing else on the site bundles SCBincludes/ following the Plugin Handbook layoutwp_pagenavi_capability filter over the capability the settings screen requireswp_pagenavi_version row holding the plugin and schema version markers, so a future upgrade has something to compare againstwp_kses_post() removes an SVG rather than cleaning it, which left the link text empty, and a link with empty text is dropped altogether. The navigation text is now filtered against wp_kses_post()‘s list plus the inline SVG elements, exposed as the wp_pagenavi_allowed_html filter. Reported by Cal at toolshed (#73)esc_url() instead of esc_attr()WP_User_Query are cast to integers, so the current page is marked correctlywp_pagenavi_class_* filters, the wp_pagenavi filter, wp_pagenavi() and wp_pagenavi_dropdown() all keep the names they shipped with| Version | Download | Type |
|---|---|---|
| 3.0.0 | Download | Stable |
| 2.94.6 | Download | Stable |
| 2.94.5 | Download | Stable |
| 2.94.4 | Download | Stable |
| 2.94.3 | Download | Stable |
| 2.94.2 | Download | Stable |
| 2.94.1 | Download | Stable |
| 2.94.0 | Download | Stable |
| 2.93.4 | Download | Stable |
| 2.93.3 | Download | Stable |
| 2.93.2 | Download | Stable |
| 2.93.1 | Download | Stable |
| 2.93 | Download | Stable |
| 2.92 | Download | Stable |
| 2.91 | Download | Stable |
| 2.90 | Download | Stable |
| 2.89.1 | Download | Stable |
| 2.89 | Download | Stable |
| 2.88 | Download | Stable |
| 2.87 | Download | Stable |
| 2.86 | Download | Stable |
| 2.85 | Download | Stable |
| 2.83 | Download | Stable |
| 2.82 | Download | Stable |
| 2.81 | Download | Stable |
| 2.80 | Download | Stable |
| 2.74 | Download | Stable |
| 2.73 | Download | Stable |
| 2.72 | Download | Stable |
| 2.61 | Download | Stable |
| 2.50 | Download | Stable |
| 2.40 | Download | Stable |
| 2.31 | Download | Stable |
| 2.30 | Download | Stable |
| 2.20 | Download | Stable |
| 2.11 | Download | Stable |
| 2.10 | Download | Stable |
| 2.03 | Download | Stable |
| 2.02 | Download | Stable |
| 2.01 | Download | Stable |
| 2.00 | Download | Stable |
| 1.00 | Download | Stable |
| Development | Download | Trunk |