I’ll build this as a reusable, safe WordPress template-part component for your RX Theme, with numeric pagination, previous/next links, archive/search support, accessibility, schema-ready markup, and helper fallbacks.
Below is a large advanced pagination.php component for your RX Theme.
Place this file here:
rx-theme/template-parts/components/pagination.php
Use it in templates like:
<?php get_template_part( 'template-parts/components/pagination' ); ?>
Or with custom args:
<?php
get_template_part(
'template-parts/components/pagination',
null,
array(
'query' => $custom_query,
'style' => 'numbers',
'show_info' => true,
'show_first' => true,
'show_last' => true,
)
);
?>
template-parts/components/pagination.php
<?php
/**
* RX Theme Advanced Pagination Component
*
* File: template-parts/components/pagination.php
*
* Features:
* - Main query pagination support
* - Custom WP_Query support via $args['query']
* - Numeric pagination
* - Previous / Next links
* - First / Last links
* - Page info text
* - Posts count info
* - Search/archive friendly
* - Accessibility ready
* - SEO-friendly rel attributes
* - Schema-friendly nav wrapper
* - Safe escaping
* - RTL-friendly arrow support
* - WooCommerce-friendly fallback support
* - AJAX-ready data attributes
* - Infinite-scroll-ready hooks/classes
* - Load-more-ready structure
*
* Usage:
*
* Basic:
* get_template_part( 'template-parts/components/pagination' );
*
* Custom:
* get_template_part(
* 'template-parts/components/pagination',
* null,
* array(
* 'query' => $custom_query,
* 'style' => 'numbers',
* 'show_info' => true,
* 'show_first' => true,
* 'show_last' => true,
* )
* );
*
* @package RX_Theme
*/
defined( 'ABSPATH' ) || exit;
/**
* ------------------------------------------------------------
* 1. Safe default arguments
* ------------------------------------------------------------
*/
$rx_pagination_defaults = array(
/**
* Query object.
*
* Accepts:
* - null: use global $wp_query
* - WP_Query object
*/
'query' => null,
/**
* Pagination style.
*
* Supported:
* - numbers
* - simple
* - compact
* - load_more_markup
*/
'style' => 'numbers',
/**
* Wrapper options.
*/
'wrapper_class' => 'rx-pagination',
'inner_class' => 'rx-pagination__inner',
'list_class' => 'rx-pagination__list',
'item_class' => 'rx-pagination__item',
'link_class' => 'rx-pagination__link',
'current_class' => 'rx-pagination__current',
'disabled_class' => 'rx-pagination__disabled',
'nav_label' => esc_html__( 'Posts navigation', 'rx-theme' ),
'aria_label' => esc_html__( 'Pagination', 'rx-theme' ),
'container_id' => '',
'extra_attributes' => array(),
/**
* Text labels.
*/
'previous_text' => esc_html__( 'Previous', 'rx-theme' ),
'next_text' => esc_html__( 'Next', 'rx-theme' ),
'first_text' => esc_html__( 'First', 'rx-theme' ),
'last_text' => esc_html__( 'Last', 'rx-theme' ),
'page_text' => esc_html__( 'Page', 'rx-theme' ),
'of_text' => esc_html__( 'of', 'rx-theme' ),
'posts_text' => esc_html__( 'posts', 'rx-theme' ),
'load_more_text' => esc_html__( 'Load More', 'rx-theme' ),
'no_more_text' => esc_html__( 'No more posts', 'rx-theme' ),
/**
* Icons.
*/
'show_icons' => true,
'previous_icon_ltr' => '←',
'next_icon_ltr' => '→',
'previous_icon_rtl' => '→',
'next_icon_rtl' => '←',
/**
* Display controls.
*/
'show_previous' => true,
'show_next' => true,
'show_first' => true,
'show_last' => true,
'show_numbers' => true,
'show_info' => true,
'show_post_count' => true,
'show_if_single' => false,
'show_dots' => true,
/**
* Number options.
*/
'mid_size' => 2,
'end_size' => 1,
/**
* Behavior options.
*/
'add_rel' => true,
'add_schema' => true,
'ajax_ready' => true,
'infinite_ready' => true,
'load_more_ready' => true,
'scroll_to_top' => false,
/**
* Query vars.
*/
'paged_var' => 'paged',
/**
* Extra CSS utility classes.
*/
'alignment' => 'center', // left, center, right, between
'size' => 'md', // sm, md, lg
'rounded' => true,
'bordered' => true,
'shadow' => false,
/**
* Developer hooks.
*/
'before' => '',
'after' => '',
);
$rx_pagination_args = isset( $args ) && is_array( $args )
? wp_parse_args( $args, $rx_pagination_defaults )
: $rx_pagination_defaults;
/**
* Allow child themes/plugins to filter pagination args.
*/
$rx_pagination_args = apply_filters( 'rx_theme_pagination_args', $rx_pagination_args );
/**
* ------------------------------------------------------------
* 2. Query detection
* ------------------------------------------------------------
*/
global $wp_query;
$rx_query = null;
if ( isset( $rx_pagination_args['query'] ) && $rx_pagination_args['query'] instanceof WP_Query ) {
$rx_query = $rx_pagination_args['query'];
} else {
$rx_query = $wp_query;
}
if ( ! $rx_query instanceof WP_Query ) {
return;
}
/**
* ------------------------------------------------------------
* 3. Current page and total page detection
* ------------------------------------------------------------
*/
$rx_total_pages = isset( $rx_query->max_num_pages ) ? absint( $rx_query->max_num_pages ) : 1;
$rx_current_page = max(
1,
absint(
get_query_var( 'paged' )
? get_query_var( 'paged' )
: get_query_var( 'page' )
)
);
/**
* Fix custom query page detection.
*/
if ( isset( $rx_pagination_args['current'] ) && absint( $rx_pagination_args['current'] ) > 0 ) {
$rx_current_page = absint( $rx_pagination_args['current'] );
}
/**
* Do not show pagination if there is only one page,
* unless show_if_single is enabled.
*/
if ( $rx_total_pages <= 1 && empty( $rx_pagination_args['show_if_single'] ) ) {
return;
}
/**
* ------------------------------------------------------------
* 4. Pagination URL base
* ------------------------------------------------------------
*/
$rx_big = 999999999;
/**
* Standard WordPress pagination base.
*/
$rx_base = str_replace(
$rx_big,
'%#%',
esc_url_raw(
get_pagenum_link( $rx_big )
)
);
/**
* Custom base support.
*/
if ( ! empty( $rx_pagination_args['base'] ) ) {
$rx_base = esc_url_raw( $rx_pagination_args['base'] );
}
/**
* Pagination format.
*/
$rx_format = '';
if ( get_option( 'permalink_structure' ) ) {
$rx_format = 'page/%#%/';
} else {
$rx_format = '?paged=%#%';
}
if ( ! empty( $rx_pagination_args['format'] ) ) {
$rx_format = sanitize_text_field( $rx_pagination_args['format'] );
}
/**
* ------------------------------------------------------------
* 5. Utility values
* ------------------------------------------------------------
*/
$rx_is_rtl = is_rtl();
$rx_previous_icon = $rx_is_rtl ? $rx_pagination_args['previous_icon_rtl'] : $rx_pagination_args['previous_icon_ltr'];
$rx_next_icon = $rx_is_rtl ? $rx_pagination_args['next_icon_rtl'] : $rx_pagination_args['next_icon_ltr'];
$rx_previous_page = max( 1, $rx_current_page - 1 );
$rx_next_page = min( $rx_total_pages, $rx_current_page + 1 );
$rx_has_previous = $rx_current_page > 1;
$rx_has_next = $rx_current_page < $rx_total_pages;
$rx_found_posts = isset( $rx_query->found_posts ) ? absint( $rx_query->found_posts ) : 0;
$rx_posts_per_page = absint( $rx_query->get( 'posts_per_page' ) );
if ( $rx_posts_per_page <= 0 ) {
$rx_posts_per_page = absint( get_option( 'posts_per_page' ) );
}
$rx_start_post = 0;
$rx_end_post = 0;
if ( $rx_found_posts > 0 && $rx_posts_per_page > 0 ) {
$rx_start_post = ( ( $rx_current_page - 1 ) * $rx_posts_per_page ) + 1;
$rx_end_post = min( $rx_current_page * $rx_posts_per_page, $rx_found_posts );
}
/**
* ------------------------------------------------------------
* 6. CSS class builder
* ------------------------------------------------------------
*/
$rx_wrapper_classes = array(
$rx_pagination_args['wrapper_class'],
'rx-pagination--style-' . sanitize_html_class( $rx_pagination_args['style'] ),
'rx-pagination--align-' . sanitize_html_class( $rx_pagination_args['alignment'] ),
'rx-pagination--size-' . sanitize_html_class( $rx_pagination_args['size'] ),
);
if ( ! empty( $rx_pagination_args['rounded'] ) ) {
$rx_wrapper_classes[] = 'rx-pagination--rounded';
}
if ( ! empty( $rx_pagination_args['bordered'] ) ) {
$rx_wrapper_classes[] = 'rx-pagination--bordered';
}
if ( ! empty( $rx_pagination_args['shadow'] ) ) {
$rx_wrapper_classes[] = 'rx-pagination--shadow';
}
if ( ! empty( $rx_pagination_args['ajax_ready'] ) ) {
$rx_wrapper_classes[] = 'rx-pagination--ajax-ready';
}
if ( ! empty( $rx_pagination_args['infinite_ready'] ) ) {
$rx_wrapper_classes[] = 'rx-pagination--infinite-ready';
}
if ( ! empty( $rx_pagination_args['load_more_ready'] ) ) {
$rx_wrapper_classes[] = 'rx-pagination--load-more-ready';
}
$rx_wrapper_classes = array_filter( array_map( 'sanitize_html_class', $rx_wrapper_classes ) );
/**
* ------------------------------------------------------------
* 7. Attribute builder
* ------------------------------------------------------------
*/
$rx_attributes = array(
'class' => implode( ' ', $rx_wrapper_classes ),
'aria-label' => $rx_pagination_args['aria_label'],
'data-current-page' => $rx_current_page,
'data-total-pages' => $rx_total_pages,
'data-found-posts' => $rx_found_posts,
);
if ( ! empty( $rx_pagination_args['container_id'] ) ) {
$rx_attributes['id'] = sanitize_html_class( $rx_pagination_args['container_id'] );
}
if ( ! empty( $rx_pagination_args['scroll_to_top'] ) ) {
$rx_attributes['data-scroll-to-top'] = 'true';
}
if ( ! empty( $rx_pagination_args['extra_attributes'] ) && is_array( $rx_pagination_args['extra_attributes'] ) ) {
foreach ( $rx_pagination_args['extra_attributes'] as $rx_attr_key => $rx_attr_value ) {
$rx_attr_key = sanitize_key( $rx_attr_key );
if ( '' !== $rx_attr_key && ! isset( $rx_attributes[ $rx_attr_key ] ) ) {
$rx_attributes[ $rx_attr_key ] = sanitize_text_field( $rx_attr_value );
}
}
}
$rx_attribute_html = '';
foreach ( $rx_attributes as $rx_attr_key => $rx_attr_value ) {
if ( '' === $rx_attr_value || null === $rx_attr_value ) {
continue;
}
$rx_attribute_html .= sprintf(
' %1$s="%2$s"',
esc_attr( $rx_attr_key ),
esc_attr( $rx_attr_value )
);
}
/**
* ------------------------------------------------------------
* 8. Helper function fallback
* ------------------------------------------------------------
*/
if ( ! function_exists( 'rx_theme_pagination_url' ) ) {
/**
* Build pagination page URL.
*
* @param int $page Page number.
* @param string $base Pagination base.
* @param string $format Pagination format.
* @return string
*/
function rx_theme_pagination_url( $page, $base, $format = '' ) {
$page = max( 1, absint( $page ) );
if ( 1 === $page ) {
$url = get_pagenum_link( 1 );
} else {
$url = str_replace( '%#%', $page, $base );
}
return esc_url( $url );
}
}
if ( ! function_exists( 'rx_theme_pagination_rel' ) ) {
/**
* Get rel attribute for pagination links.
*
* @param int $page Current link page.
* @param int $current Current page.
* @param int $total Total pages.
* @return string
*/
function rx_theme_pagination_rel( $page, $current, $total ) {
$page = absint( $page );
$current = absint( $current );
$total = absint( $total );
if ( $page === $current - 1 ) {
return 'prev';
}
if ( $page === $current + 1 ) {
return 'next';
}
return '';
}
}
if ( ! function_exists( 'rx_theme_pagination_item' ) ) {
/**
* Print one pagination item.
*
* @param array $item Item args.
* @return void
*/
function rx_theme_pagination_item( $item ) {
$defaults = array(
'type' => 'number',
'url' => '',
'label' => '',
'aria_label' => '',
'class' => '',
'link_class' => '',
'current' => false,
'disabled' => false,
'rel' => '',
'page' => 0,
'item_class' => 'rx-pagination__item',
'current_class' => 'rx-pagination__current',
'disabled_class'=> 'rx-pagination__disabled',
);
$item = wp_parse_args( $item, $defaults );
$item_classes = array(
$item['item_class'],
'rx-pagination__item--' . sanitize_html_class( $item['type'] ),
);
if ( ! empty( $item['class'] ) ) {
$item_classes[] = sanitize_html_class( $item['class'] );
}
if ( ! empty( $item['current'] ) ) {
$item_classes[] = sanitize_html_class( $item['current_class'] );
$item_classes[] = 'is-current';
}
if ( ! empty( $item['disabled'] ) ) {
$item_classes[] = sanitize_html_class( $item['disabled_class'] );
$item_classes[] = 'is-disabled';
}
$item_classes = array_filter( $item_classes );
echo '<li class="' . esc_attr( implode( ' ', $item_classes ) ) . '">';
if ( ! empty( $item['current'] ) ) {
echo '<span class="' . esc_attr( $item['link_class'] ) . '" aria-current="page">';
echo wp_kses_post( $item['label'] );
echo '</span>';
} elseif ( ! empty( $item['disabled'] ) || empty( $item['url'] ) ) {
echo '<span class="' . esc_attr( $item['link_class'] ) . '" aria-disabled="true">';
echo wp_kses_post( $item['label'] );
echo '</span>';
} else {
$rel_html = '';
if ( ! empty( $item['rel'] ) ) {
$rel_html = ' rel="' . esc_attr( $item['rel'] ) . '"';
}
$page_html = '';
if ( ! empty( $item['page'] ) ) {
$page_html = ' data-page="' . esc_attr( absint( $item['page'] ) ) . '"';
}
echo '<a class="' . esc_attr( $item['link_class'] ) . '" href="' . esc_url( $item['url'] ) . '"' . $rel_html . $page_html . ' aria-label="' . esc_attr( $item['aria_label'] ) . '">';
echo wp_kses_post( $item['label'] );
echo '</a>';
}
echo '</li>';
}
}
/**
* ------------------------------------------------------------
* 9. Build numeric pagination array
* ------------------------------------------------------------
*/
$rx_number_links = paginate_links(
array(
'base' => $rx_base,
'format' => $rx_format,
'current' => $rx_current_page,
'total' => $rx_total_pages,
'mid_size' => absint( $rx_pagination_args['mid_size'] ),
'end_size' => absint( $rx_pagination_args['end_size'] ),
'type' => 'array',
'prev_next' => false,
)
);
if ( ! is_array( $rx_number_links ) ) {
$rx_number_links = array();
}
/**
* ------------------------------------------------------------
* 10. Schema attributes
* ------------------------------------------------------------
*/
$rx_schema_html = '';
if ( ! empty( $rx_pagination_args['add_schema'] ) ) {
$rx_schema_html = ' itemscope itemtype="https://schema.org/SiteNavigationElement"';
}
/**
* ------------------------------------------------------------
* 11. Developer hook before render
* ------------------------------------------------------------
*/
do_action(
'rx_theme_before_pagination',
$rx_pagination_args,
$rx_query,
$rx_current_page,
$rx_total_pages
);
echo wp_kses_post( $rx_pagination_args['before'] );
?>
<nav<?php echo $rx_attribute_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo $rx_schema_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
<div class="<?php echo esc_attr( $rx_pagination_args['inner_class'] ); ?>">
<?php if ( ! empty( $rx_pagination_args['show_info'] ) || ! empty( $rx_pagination_args['show_post_count'] ) ) : ?>
<div class="rx-pagination__meta">
<?php if ( ! empty( $rx_pagination_args['show_info'] ) ) : ?>
<p class="rx-pagination__page-info">
<span class="rx-pagination__page-info-label">
<?php echo esc_html( $rx_pagination_args['page_text'] ); ?>
</span>
<span class="rx-pagination__page-info-current">
<?php echo esc_html( number_format_i18n( $rx_current_page ) ); ?>
</span>
<span class="rx-pagination__page-info-of">
<?php echo esc_html( $rx_pagination_args['of_text'] ); ?>
</span>
<span class="rx-pagination__page-info-total">
<?php echo esc_html( number_format_i18n( $rx_total_pages ) ); ?>
</span>
</p>
<?php endif; ?>
<?php if ( ! empty( $rx_pagination_args['show_post_count'] ) && $rx_found_posts > 0 ) : ?>
<p class="rx-pagination__post-count">
<span class="rx-pagination__post-count-range">
<?php
echo esc_html(
sprintf(
/* translators: 1: first post number, 2: last post number */
__( 'Showing %1$s–%2$s', 'rx-theme' ),
number_format_i18n( $rx_start_post ),
number_format_i18n( $rx_end_post )
)
);
?>
</span>
<span class="rx-pagination__post-count-total">
<?php
echo esc_html(
sprintf(
/* translators: 1: total posts, 2: posts text */
__( 'of %1$s %2$s', 'rx-theme' ),
number_format_i18n( $rx_found_posts ),
$rx_pagination_args['posts_text']
)
);
?>
</span>
</p>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ( 'simple' === $rx_pagination_args['style'] ) : ?>
<div class="rx-pagination__simple">
<?php if ( ! empty( $rx_pagination_args['show_previous'] ) ) : ?>
<?php if ( $rx_has_previous ) : ?>
<a
class="rx-pagination__simple-link rx-pagination__simple-link--previous"
href="<?php echo esc_url( rx_theme_pagination_url( $rx_previous_page, $rx_base, $rx_format ) ); ?>"
rel="prev"
data-page="<?php echo esc_attr( $rx_previous_page ); ?>"
aria-label="<?php echo esc_attr__( 'Go to previous page', 'rx-theme' ); ?>"
>
<?php if ( ! empty( $rx_pagination_args['show_icons'] ) ) : ?>
<span class="rx-pagination__icon" aria-hidden="true"><?php echo wp_kses_post( $rx_previous_icon ); ?></span>
<?php endif; ?>
<span><?php echo esc_html( $rx_pagination_args['previous_text'] ); ?></span>
</a>
<?php else : ?>
<span class="rx-pagination__simple-link rx-pagination__simple-link--previous is-disabled" aria-disabled="true">
<?php if ( ! empty( $rx_pagination_args['show_icons'] ) ) : ?>
<span class="rx-pagination__icon" aria-hidden="true"><?php echo wp_kses_post( $rx_previous_icon ); ?></span>
<?php endif; ?>
<span><?php echo esc_html( $rx_pagination_args['previous_text'] ); ?></span>
</span>
<?php endif; ?>
<?php endif; ?>
<?php if ( ! empty( $rx_pagination_args['show_next'] ) ) : ?>
<?php if ( $rx_has_next ) : ?>
<a
class="rx-pagination__simple-link rx-pagination__simple-link--next"
href="<?php echo esc_url( rx_theme_pagination_url( $rx_next_page, $rx_base, $rx_format ) ); ?>"
rel="next"
data-page="<?php echo esc_attr( $rx_next_page ); ?>"
aria-label="<?php echo esc_attr__( 'Go to next page', 'rx-theme' ); ?>"
>
<span><?php echo esc_html( $rx_pagination_args['next_text'] ); ?></span>
<?php if ( ! empty( $rx_pagination_args['show_icons'] ) ) : ?>
<span class="rx-pagination__icon" aria-hidden="true"><?php echo wp_kses_post( $rx_next_icon ); ?></span>
<?php endif; ?>
</a>
<?php else : ?>
<span class="rx-pagination__simple-link rx-pagination__simple-link--next is-disabled" aria-disabled="true">
<span><?php echo esc_html( $rx_pagination_args['next_text'] ); ?></span>
<?php if ( ! empty( $rx_pagination_args['show_icons'] ) ) : ?>
<span class="rx-pagination__icon" aria-hidden="true"><?php echo wp_kses_post( $rx_next_icon ); ?></span>
<?php endif; ?>
</span>
<?php endif; ?>
<?php endif; ?>
</div>
<?php elseif ( 'compact' === $rx_pagination_args['style'] ) : ?>
<div class="rx-pagination__compact">
<?php if ( $rx_has_previous ) : ?>
<a
class="rx-pagination__compact-button rx-pagination__compact-button--previous"
href="<?php echo esc_url( rx_theme_pagination_url( $rx_previous_page, $rx_base, $rx_format ) ); ?>"
rel="prev"
data-page="<?php echo esc_attr( $rx_previous_page ); ?>"
aria-label="<?php echo esc_attr__( 'Previous page', 'rx-theme' ); ?>"
>
<?php echo wp_kses_post( $rx_previous_icon ); ?>
</a>
<?php else : ?>
<span class="rx-pagination__compact-button rx-pagination__compact-button--previous is-disabled" aria-disabled="true">
<?php echo wp_kses_post( $rx_previous_icon ); ?>
</span>
<?php endif; ?>
<span class="rx-pagination__compact-status">
<?php
echo esc_html(
sprintf(
/* translators: 1: current page, 2: total pages */
__( '%1$s / %2$s', 'rx-theme' ),
number_format_i18n( $rx_current_page ),
number_format_i18n( $rx_total_pages )
)
);
?>
</span>
<?php if ( $rx_has_next ) : ?>
<a
class="rx-pagination__compact-button rx-pagination__compact-button--next"
href="<?php echo esc_url( rx_theme_pagination_url( $rx_next_page, $rx_base, $rx_format ) ); ?>"
rel="next"
data-page="<?php echo esc_attr( $rx_next_page ); ?>"
aria-label="<?php echo esc_attr__( 'Next page', 'rx-theme' ); ?>"
>
<?php echo wp_kses_post( $rx_next_icon ); ?>
</a>
<?php else : ?>
<span class="rx-pagination__compact-button rx-pagination__compact-button--next is-disabled" aria-disabled="true">
<?php echo wp_kses_post( $rx_next_icon ); ?>
</span>
<?php endif; ?>
</div>
<?php elseif ( 'load_more_markup' === $rx_pagination_args['style'] ) : ?>
<div class="rx-pagination__load-more-wrap">
<?php if ( $rx_has_next ) : ?>
<a
class="rx-pagination__load-more-button"
href="<?php echo esc_url( rx_theme_pagination_url( $rx_next_page, $rx_base, $rx_format ) ); ?>"
rel="next"
data-page="<?php echo esc_attr( $rx_next_page ); ?>"
data-current-page="<?php echo esc_attr( $rx_current_page ); ?>"
data-total-pages="<?php echo esc_attr( $rx_total_pages ); ?>"
aria-label="<?php echo esc_attr__( 'Load more posts', 'rx-theme' ); ?>"
>
<span class="rx-pagination__load-more-text">
<?php echo esc_html( $rx_pagination_args['load_more_text'] ); ?>
</span>
<span class="rx-pagination__load-more-icon" aria-hidden="true">
+
</span>
</a>
<?php else : ?>
<span class="rx-pagination__load-more-button is-disabled" aria-disabled="true">
<span class="rx-pagination__load-more-text">
<?php echo esc_html( $rx_pagination_args['no_more_text'] ); ?>
</span>
</span>
<?php endif; ?>
</div>
<?php else : ?>
<ul class="<?php echo esc_attr( $rx_pagination_args['list_class'] ); ?>">
<?php
/**
* First page link.
*/
if ( ! empty( $rx_pagination_args['show_first'] ) ) {
rx_theme_pagination_item(
array(
'type' => 'first',
'url' => $rx_has_previous ? rx_theme_pagination_url( 1, $rx_base, $rx_format ) : '',
'label' => esc_html( $rx_pagination_args['first_text'] ),
'aria_label' => esc_html__( 'Go to first page', 'rx-theme' ),
'link_class' => $rx_pagination_args['link_class'],
'disabled' => ! $rx_has_previous,
'page' => 1,
'item_class' => $rx_pagination_args['item_class'],
'current_class' => $rx_pagination_args['current_class'],
'disabled_class' => $rx_pagination_args['disabled_class'],
)
);
}
/**
* Previous page link.
*/
if ( ! empty( $rx_pagination_args['show_previous'] ) ) {
$rx_previous_label = '';
if ( ! empty( $rx_pagination_args['show_icons'] ) ) {
$rx_previous_label .= '<span class="rx-pagination__icon" aria-hidden="true">' . wp_kses_post( $rx_previous_icon ) . '</span> ';
}
$rx_previous_label .= '<span class="rx-pagination__text">' . esc_html( $rx_pagination_args['previous_text'] ) . '</span>';
rx_theme_pagination_item(
array(
'type' => 'previous',
'url' => $rx_has_previous ? rx_theme_pagination_url( $rx_previous_page, $rx_base, $rx_format ) : '',
'label' => $rx_previous_label,
'aria_label' => esc_html__( 'Go to previous page', 'rx-theme' ),
'link_class' => $rx_pagination_args['link_class'],
'disabled' => ! $rx_has_previous,
'rel' => 'prev',
'page' => $rx_previous_page,
'item_class' => $rx_pagination_args['item_class'],
'current_class' => $rx_pagination_args['current_class'],
'disabled_class' => $rx_pagination_args['disabled_class'],
)
);
}
/**
* Number links.
*/
if ( ! empty( $rx_pagination_args['show_numbers'] ) ) {
foreach ( $rx_number_links as $rx_link ) {
$rx_is_current = false;
$rx_is_dots = false;
$rx_page_num = 0;
$rx_url = '';
if ( false !== strpos( $rx_link, 'current' ) ) {
$rx_is_current = true;
$rx_page_num = $rx_current_page;
}
if ( false !== strpos( $rx_link, 'dots' ) ) {
$rx_is_dots = true;
}
if ( preg_match( '/page-numbers[^>]*>([0-9]+)</', $rx_link, $rx_matches ) ) {
$rx_page_num = absint( $rx_matches[1] );
}
if ( preg_match( '/href=[\'"]([^\'"]+)[\'"]/', $rx_link, $rx_url_matches ) ) {
$rx_url = esc_url_raw( html_entity_decode( $rx_url_matches[1] ) );
}
if ( $rx_is_dots ) {
if ( ! empty( $rx_pagination_args['show_dots'] ) ) {
rx_theme_pagination_item(
array(
'type' => 'dots',
'label' => '…',
'aria_label' => esc_html__( 'Pagination dots', 'rx-theme' ),
'link_class' => $rx_pagination_args['link_class'],
'disabled' => true,
'item_class' => $rx_pagination_args['item_class'],
'current_class' => $rx_pagination_args['current_class'],
'disabled_class' => $rx_pagination_args['disabled_class'],
)
);
}
continue;
}
if ( $rx_page_num <= 0 ) {
continue;
}
$rx_rel = '';
if ( ! empty( $rx_pagination_args['add_rel'] ) ) {
$rx_rel = rx_theme_pagination_rel( $rx_page_num, $rx_current_page, $rx_total_pages );
}
rx_theme_pagination_item(
array(
'type' => 'number',
'url' => $rx_is_current ? '' : $rx_url,
'label' => number_format_i18n( $rx_page_num ),
'aria_label' => sprintf(
/* translators: %s: page number */
esc_html__( 'Go to page %s', 'rx-theme' ),
number_format_i18n( $rx_page_num )
),
'link_class' => $rx_pagination_args['link_class'],
'current' => $rx_is_current,
'rel' => $rx_rel,
'page' => $rx_page_num,
'item_class' => $rx_pagination_args['item_class'],
'current_class' => $rx_pagination_args['current_class'],
'disabled_class' => $rx_pagination_args['disabled_class'],
)
);
}
}
/**
* Next page link.
*/
if ( ! empty( $rx_pagination_args['show_next'] ) ) {
$rx_next_label = '<span class="rx-pagination__text">' . esc_html( $rx_pagination_args['next_text'] ) . '</span>';
if ( ! empty( $rx_pagination_args['show_icons'] ) ) {
$rx_next_label .= ' <span class="rx-pagination__icon" aria-hidden="true">' . wp_kses_post( $rx_next_icon ) . '</span>';
}
rx_theme_pagination_item(
array(
'type' => 'next',
'url' => $rx_has_next ? rx_theme_pagination_url( $rx_next_page, $rx_base, $rx_format ) : '',
'label' => $rx_next_label,
'aria_label' => esc_html__( 'Go to next page', 'rx-theme' ),
'link_class' => $rx_pagination_args['link_class'],
'disabled' => ! $rx_has_next,
'rel' => 'next',
'page' => $rx_next_page,
'item_class' => $rx_pagination_args['item_class'],
'current_class' => $rx_pagination_args['current_class'],
'disabled_class' => $rx_pagination_args['disabled_class'],
)
);
}
/**
* Last page link.
*/
if ( ! empty( $rx_pagination_args['show_last'] ) ) {
rx_theme_pagination_item(
array(
'type' => 'last',
'url' => $rx_has_next ? rx_theme_pagination_url( $rx_total_pages, $rx_base, $rx_format ) : '',
'label' => esc_html( $rx_pagination_args['last_text'] ),
'aria_label' => esc_html__( 'Go to last page', 'rx-theme' ),
'link_class' => $rx_pagination_args['link_class'],
'disabled' => ! $rx_has_next,
'page' => $rx_total_pages,
'item_class' => $rx_pagination_args['item_class'],
'current_class' => $rx_pagination_args['current_class'],
'disabled_class' => $rx_pagination_args['disabled_class'],
)
);
}
?>
</ul>
<?php endif; ?>
<?php if ( ! empty( $rx_pagination_args['infinite_ready'] ) ) : ?>
<span
class="rx-pagination__sentinel"
aria-hidden="true"
data-rx-pagination-sentinel="true"
data-next-page="<?php echo esc_attr( $rx_has_next ? $rx_next_page : 0 ); ?>"
data-total-pages="<?php echo esc_attr( $rx_total_pages ); ?>"
></span>
<?php endif; ?>
</div>
</nav>
<?php
echo wp_kses_post( $rx_pagination_args['after'] );
do_action(
'rx_theme_after_pagination',
$rx_pagination_args,
$rx_query,
$rx_current_page,
$rx_total_pages
);
Optional CSS for style.css
Add this to your RX Theme style.css or pagination CSS file:
.rx-pagination {
margin: 2.5rem 0;
width: 100%;
}
.rx-pagination__inner {
display: flex;
flex-direction: column;
gap: 1rem;
}
.rx-pagination__meta {
display: flex;
flex-wrap: wrap;
gap: 0.75rem 1.25rem;
align-items: center;
justify-content: center;
font-size: 0.9375rem;
color: var(--rx-color-text-muted, #667085);
}
.rx-pagination__page-info,
.rx-pagination__post-count {
margin: 0;
}
.rx-pagination__list {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
justify-content: center;
margin: 0;
padding: 0;
list-style: none;
}
.rx-pagination--align-left .rx-pagination__list,
.rx-pagination--align-left .rx-pagination__meta {
justify-content: flex-start;
}
.rx-pagination--align-center .rx-pagination__list,
.rx-pagination--align-center .rx-pagination__meta {
justify-content: center;
}
.rx-pagination--align-right .rx-pagination__list,
.rx-pagination--align-right .rx-pagination__meta {
justify-content: flex-end;
}
.rx-pagination--align-between .rx-pagination__list,
.rx-pagination--align-between .rx-pagination__meta {
justify-content: space-between;
}
.rx-pagination__item {
display: inline-flex;
}
.rx-pagination__link,
.rx-pagination__current,
.rx-pagination__disabled,
.rx-pagination__simple-link,
.rx-pagination__compact-button,
.rx-pagination__load-more-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
min-width: 2.5rem;
min-height: 2.5rem;
padding: 0.55rem 0.85rem;
border: 1px solid var(--rx-color-border, #d0d5dd);
border-radius: 0.5rem;
background: var(--rx-color-surface, #ffffff);
color: var(--rx-color-text, #101828);
font-size: 0.9375rem;
font-weight: 600;
line-height: 1;
text-decoration: none;
transition:
background-color 0.2s ease,
color 0.2s ease,
border-color 0.2s ease,
box-shadow 0.2s ease,
transform 0.2s ease;
}
.rx-pagination__link:hover,
.rx-pagination__simple-link:hover,
.rx-pagination__compact-button:hover,
.rx-pagination__load-more-button:hover {
background: var(--rx-color-primary, #2563eb);
border-color: var(--rx-color-primary, #2563eb);
color: #ffffff;
text-decoration: none;
transform: translateY(-1px);
}
.rx-pagination__link:focus-visible,
.rx-pagination__simple-link:focus-visible,
.rx-pagination__compact-button:focus-visible,
.rx-pagination__load-more-button:focus-visible {
outline: 3px solid rgba(37, 99, 235, 0.25);
outline-offset: 2px;
}
.rx-pagination__current,
.rx-pagination .is-current > span {
background: var(--rx-color-primary, #2563eb);
border-color: var(--rx-color-primary, #2563eb);
color: #ffffff;
cursor: default;
}
.rx-pagination__disabled,
.rx-pagination .is-disabled > span,
.rx-pagination .is-disabled {
opacity: 0.45;
cursor: not-allowed;
pointer-events: none;
}
.rx-pagination--size-sm .rx-pagination__link,
.rx-pagination--size-sm .rx-pagination__current,
.rx-pagination--size-sm .rx-pagination__disabled {
min-width: 2rem;
min-height: 2rem;
padding: 0.4rem 0.65rem;
font-size: 0.8125rem;
}
.rx-pagination--size-lg .rx-pagination__link,
.rx-pagination--size-lg .rx-pagination__current,
.rx-pagination--size-lg .rx-pagination__disabled {
min-width: 3rem;
min-height: 3rem;
padding: 0.75rem 1rem;
font-size: 1rem;
}
.rx-pagination--rounded .rx-pagination__link,
.rx-pagination--rounded .rx-pagination__current,
.rx-pagination--rounded .rx-pagination__disabled,
.rx-pagination--rounded .rx-pagination__simple-link,
.rx-pagination--rounded .rx-pagination__compact-button,
.rx-pagination--rounded .rx-pagination__load-more-button {
border-radius: 999px;
}
.rx-pagination--shadow .rx-pagination__link,
.rx-pagination--shadow .rx-pagination__current,
.rx-pagination--shadow .rx-pagination__disabled,
.rx-pagination--shadow .rx-pagination__simple-link,
.rx-pagination--shadow .rx-pagination__compact-button,
.rx-pagination--shadow .rx-pagination__load-more-button {
box-shadow: 0 8px 20px rgba(16, 24, 40, 0.08);
}
.rx-pagination__simple,
.rx-pagination__compact,
.rx-pagination__load-more-wrap {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
align-items: center;
justify-content: center;
}
.rx-pagination__compact-status {
font-weight: 700;
color: var(--rx-color-text, #101828);
}
.rx-pagination__load-more-button {
min-width: 11rem;
}
.rx-pagination__sentinel {
display: block;
width: 100%;
height: 1px;
pointer-events: none;
}
@media (max-width: 575px) {
.rx-pagination__meta {
flex-direction: column;
gap: 0.35rem;
text-align: center;
}
.rx-pagination__list {
gap: 0.35rem;
}
.rx-pagination__item--first,
.rx-pagination__item--last {
display: none;
}
.rx-pagination__link,
.rx-pagination__current,
.rx-pagination__disabled {
min-width: 2.25rem;
min-height: 2.25rem;
padding: 0.45rem 0.65rem;
font-size: 0.875rem;
}
}
Example uses
Normal archive pagination
<?php get_template_part( 'template-parts/components/pagination' ); ?>
Compact pagination
<?php
get_template_part(
'template-parts/components/pagination',
null,
array(
'style' => 'compact',
)
);
?>
Simple previous / next pagination
<?php
get_template_part(
'template-parts/components/pagination',
null,
array(
'style' => 'simple',
'show_info' => false,
'show_post_count' => false,
)
);
?>
Load more button markup
<?php
get_template_part(
'template-parts/components/pagination',
null,
array(
'style' => 'load_more_markup',
'show_info' => true,
'show_post_count' => true,
)
);
?>
Custom WP_Query pagination
<?php
$rx_custom_query = new WP_Query(
array(
'post_type' => 'post',
'posts_per_page' => 6,
'paged' => max( 1, get_query_var( 'paged' ) ),
)
);
if ( $rx_custom_query->have_posts() ) :
while ( $rx_custom_query->have_posts() ) :
$rx_custom_query->the_post();
get_template_part( 'template-parts/content/content', get_post_type() );
endwhile;
get_template_part(
'template-parts/components/pagination',
null,
array(
'query' => $rx_custom_query,
'style' => 'numbers',
'show_first' => true,
'show_last' => true,
)
);
wp_reset_postdata();
endif;
?>
This component is safe to use as your main RX Theme pagination system.