A custom WP_Query does not paginate by itself. You need to pass the current page number to the query and then print the page links with paginate_links().

Custom loop with pagination

<?php
$lotgrafix_paged = get_query_var( 'paged' ) ? absint( get_query_var( 'paged' ) ) : 1;

$lotgrafix_query = new WP_Query( array(
    'post_type'      => 'post',
    'posts_per_page' => 6,
    'paged'          => $lotgrafix_paged,
) );

if ( $lotgrafix_query->have_posts() ) :
    while ( $lotgrafix_query->have_posts() ) :
        $lotgrafix_query->the_post();
        ?>
        <article>
            <h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
        </article>
        <?php
    endwhile;

    echo '<nav class="lotgrafix-pagination">';
    echo paginate_links( array(
        'total'     => $lotgrafix_query->max_num_pages,
        'current'   => $lotgrafix_paged,
        'mid_size'  => 2,
        'prev_text' => '&laquo; Prev',
        'next_text' => 'Next &raquo;',
    ) );
    echo '</nav>';

    wp_reset_postdata();
endif;
?>

Static front page

On a page set as the static front page, WordPress uses the page variable instead of paged.

$lotgrafix_paged = get_query_var( 'paged' ) ? absint( get_query_var( 'paged' ) ) : ( get_query_var( 'page' ) ? absint( get_query_var( 'page' ) ) : 1 );

Pagination for the main loop

For blog and archive templates that use the default loop, use the built in function.

the_posts_pagination( array(
    'mid_size'  => 2,
    'prev_text' => __( 'Previous', 'lotgrafix' ),
    'next_text' => __( 'Next', 'lotgrafix' ),
) );

Simple previous and next links

the_posts_navigation();

Style the links

.lotgrafix-pagination .page-numbers {
    display: inline-block;
    padding: 6px 12px;
    border: 1px solid #ddd;
    margin-right: 4px;
}

.lotgrafix-pagination .current {
    background: orangered;
    color: #fff;
    border-color: orangered;
}
«
»