Wordpress - How to display pagination links for WP_User_Query?

This should get you really close. I haven't tested it, but it's nearly identical to a setup I've used a few times.

/*
 * We start by doing a query to retrieve all users
 * We need a total user count so that we can calculate how many pages there are
 */

$count_args  = array(
    'role'      => 'Subscriber',
    'fields'    => 'all_with_meta',
    'number'    => 999999      
);
$user_count_query = new WP_User_Query($count_args);
$user_count = $user_count_query->get_results();

// count the number of users found in the query
$total_users = $user_count ? count($user_count) : 1;

// grab the current page number and set to 1 if no page number is set
$page = isset($_GET['p']) ? $_GET['p'] : 1;

// how many users to show per page
$users_per_page = 5;

// calculate the total number of pages.
$total_pages = 1;
$offset = $users_per_page * ($page - 1);
$total_pages = ceil($total_users / $users_per_page);


// main user query
$args  = array(
    // search only for Authors role
    'role'      => 'Subscriber',
    // order results by display_name
    'orderby'   => 'display_name',
    // return all fields
    'fields'    => 'all_with_meta',
    'number'    => $users_per_page,
    'offset'    => $offset // skip the number of users that we have per page  
);

// Create the WP_User_Query object
$wp_user_query = new WP_User_Query($args);

// Get the results
$authors = $wp_user_query->get_results();

// check to see if we have users
if (!empty($authors))
{
    echo '<div class="author-entry">';
    // loop trough each author
    foreach ($authors as $author)
    {
        $author_info = get_userdata($author->ID); ?>

        <span style="float:left;padding:0 5px 0 0;"><?php echo get_avatar( $author->ID, 50 ); /* http://codex.wordpress.org/Function_Reference/get_avatar */ ?></span>
        <span class="fn"><strong>First name</strong> : <?php echo $author_info->first_name; ?></span><br />
        <span class="ln"><strong>Last name</strong> : <?php echo $author_info->last_name; ?></span><br />
        <span class="em"><strong>Email address</strong> : <a href="mailto:<?php echo $author_info->user_email; ?>"><?php echo $author_info->user_email; ?></a></span><br />
        <span class="we"><strong>Website</strong> : <a href="<?php echo $author_info->user_url; ?>"><?php echo $author_info->user_url; ?></a></span><br />

        <span class="de"><strong>Bio</strong> :<br /><?php echo $author_info->description ; ?></span>
        <div class="clear">&nbsp;</div>

    <?php 
    }
    echo '</div>';
} else {
    echo 'No authors found';
}

// grab the current query parameters
$query_string = $_SERVER['QUERY_STRING'];

// The $base variable stores the complete URL to our page, including the current page arg

// if in the admin, your base should be the admin URL + your page
$base = admin_url('your-page-path') . '?' . remove_query_arg('p', $query_string) . '%_%';

// if on the front end, your base is the current page
//$base = get_permalink( get_the_ID() ) . '?' . remove_query_arg('p', $query_string) . '%_%';

echo paginate_links( array(
    'base' => $base, // the base URL, including query arg
    'format' => '&p=%#%', // this defines the query parameter that will be used, in this case "p"
    'prev_text' => __('&laquo; Previous'), // text for previous page
    'next_text' => __('Next &raquo;'), // text for next page
    'total' => $total_pages, // the total number of pages we have
    'current' => $page, // the current page
    'end_size' => 1,
    'mid_size' => 5,
));

You really should not use the answer by Pippin. The query is very inefficient. $user_count_query in the example can return up to 999,999 users from your database to your script, with all of the user fields. This will surely hit memory and/or time limits for PHP if/when your site grows large enough.

But that may have been the only solution back in 2012.

Here is a better way to do it. In this example I've only got next and previous page but if you do need numbered pagination, the variables are there to build it out. WordPress doesn't have a pagination function that is compatible with WP_User_Query (to my knowledge).

<?php

// Pagination vars
$current_page = get_query_var('paged') ? (int) get_query_var('paged') : 1;
$users_per_page = 2; // RAISE THIS AFTER TESTING ;)

$args = array(
    'number' => $users_per_page, // How many per page
    'paged' => $current_page // What page to get, starting from 1.
);

$users = new WP_User_Query( $args );

$total_users = $users->get_total(); // How many users we have in total (beyond the current page)
$num_pages = ceil($total_users / $users_per_page); // How many pages of users we will need

?>
    <h3>Page <?php echo $current_page; ?> of <?php echo $num_pages; ?></h3>
    <p>Displaying <?php echo $users_per_page; ?> of <?php echo $total_users; ?> users</p>

    <table>
        <thead>
            <tr>
                <th>First Name</th>
                <th>Last Name</th>
                <th>Email</th>
            </tr>
        </thead>

        <tbody>
            <?php
            if ( $users->get_results() ) foreach( $users->get_results() as $user )  {
                $firstname = $user->first_name;
                $lastname = $user->last_name;
                $email = $user->user_email;
                ?>
                <tr>
                    <td><?php echo esc_html($firstname); ?></td>
                    <td><?php echo esc_html($lastname); ?></td>
                    <td><?php echo esc_html($email); ?></td>
                </tr>
                <?php
            }
            ?>
        </tbody>
    </table>

    <p>
        <?php
        // Previous page
        if ( $current_page > 1 ) {
            echo '<a href="'. add_query_arg(array('paged' => $current_page-1)) .'">Previous Page</a>';
        }

        // Next page
        if ( $current_page < $num_pages ) {
            echo '<a href="'. add_query_arg(array('paged' => $current_page+1)) .'">Next Page</a>';
        }
        ?>
    </p>

Example showing page 2:

table of users, starting at page 2


Update 6/8/2018: How to add page numbers instead of Next/Previous

If you want to have page numbers instead of next/previous page links, here is how you can set that up. Note that you will need to replace the numbers with page links, they will not be clickable in this example (based on https://stackoverflow.com/a/11274294/470480, modified to show a consistent amount of middle numbers and not add the "..." unless a page is actually skipped).

You can also see my gist file which contains a reusable function for this purpose.

$current_page = 5; // Example
$num_pages = 10; // Example

$edge_number_count = 2; // Change this, optional

$start_number = $current_page - $edge_number_count;
$end_number = $current_page + $edge_number_count;

// Minus one so that we don't split the start number unnecessarily, eg: "1 ... 2 3" should start as "1 2 3"
if ( ($start_number - 1) < 1 ) {
    $start_number = 1;
    $end_number = min($num_pages, $start_number + ($edge_number_count*2));
}

// Add one so that we don't split the end number unnecessarily, eg: "8 9 ... 10" should stay as "8 9 10"
if ( ($end_number + 1) > $num_pages ) {
    $end_number = $num_pages;
    $start_number = max(1, $num_pages - ($edge_number_count*2));
}

if ($start_number > 1) echo " 1 ... ";

for($i=$start_number; $i<=$end_number; $i++) {
    if ( $i === $current_page ) echo " [{$i}] ";
    else echo " {$i} ";
}

if ($end_number < $num_pages) echo " ... {$num_pages} ";

Output (from page 1 to 10):

[1]  2  3  4  5  ... 10 
1  [2]  3  4  5  ... 10 
1  2  [3]  4  5  ... 10 
1  2  3  [4]  5  ... 10 

1 ...  3  4  [5]  6  7  ... 10 
1 ...  4  5  [6]  7  8  ... 10 

1 ...  6  [7]  8  9  10
1 ...  6  7  [8]  9  10
1 ...  6  7  8  [9]  10
1 ...  6  7  8  9  [10]