Users instead of posts: How to modify the main query?

Custom GET pagination parameter

We can use a custom GET pagination parameter, e.g. upage or probably more unique as wpse-user-page, to use with your WP_User_Query. That way you avoid affecting e.g. the main post query.

Generate pagination links with pageinate_links()

We can use paginate_links() to generate the pagination:

$args = array(
    'base'         => '%_%',
    'format'       => '?upage=%#%',
     'total'       => $total_pages,  // e.g. ceil( $total_users / $number ); 
     'current'     => $current_page, // from our custom GET pagination parameter
);

$paginate_html = sprintf( 
    '<div class="users-pagination">%s</div>', 
    paginate_links( $args ) 
);

Where to display the user archive?

We can e.g. display it on a given page, using a shortcode or a custom page template.

Prettify the user archive’s pagination url

Here are few approaches to prettify the custom GET pagination parameter:

A) Add it as a custom endpoint to pages with:

add_rewrite_endpoint( 'upage', EP_PAGES );

by hooking this into the init action and then after saving permalinks, access the user archive from all pages:

example.tld/authoes/upage/123/
example.tld/someotherpage/asubpage/upage/123/
... etc ...

Then we fetch the current page with:

get_query_var( 'upage', 1 );

B) If we want to target a given page slug (e.g. authors) we can hook the following into the init hook:

add_rewrite_rule( 
    '^authors/upage/?([0-9]{1,})/?$', 
    'index.php?pagename=authors&upage=$matches[1]', 
    'top'
);

and then register upage as a query variable:

add_filter( 'query_vars', function( $vars ) {
    $vars[] = 'upage';
    return $vars;
} );

After saving permalinks, we access the user archive from:

example.tld/authors/upage/123/

C) If we could also override the page url part, that corresponds to the paged query variable, with:

add_rewrite_rule( 
    '^authors/page/?([0-9]{1,})/?$', 
    'index.php?pagename=authors&upage=$matches[1]', 
    'top'
);

where we register upage as a query variable as before.

Then after saving permalinks, we can access the user archive from:

example.tld/authors/page/123/

D) We could also just use the page query variable for the authors page and access the user archive with:

example.tld/authors/123/

where the current page is:

get_query_var( 'page', 1 );

Note that this would interfere with the content pagination, but there’s no limit check for that query variable.

E) We could simulate how the comments pagination is handled in core, that uses a rewrite like this one:

(.?.+?)/comment-page-([0-9]{1,})/?$

where cpage is the corresponding comment pagination query variable for the ([0-9]{1,}) match.

Alternatively, one could also consider using the REST API with javascript approach. The core ships with the wp-api Backbone client library that can handle e.g. users collection.

Hope it helps!