Spring Boot Thymeleaf Pagination

Pagination is widely used to split large data entries into discrete pages, allowing users to easily toggle through data entries. In this brief tutorial, we'll learn how to implement pagination in a Spring Boot and Thymeleaf-based application. We will display a list of customers in Thymeleaf with pagination.

Getting Started

For this tutorial, I assume you know how to create a Spring Boot project that uses the Thymeleaf templating engine. You can also use Spring Initializr to quickly create a Spring Boot project with Thymeleaf and Spring Data JPA dependencies.

Spring Data Models

Our application has only one model (also called domain in Spring Data JPA) named Customer. The model has three attributes: id, name, and address:


public class Customer {
    private Long id;

    private String name;
    private String address;

    // all-argument constructor, getters, and setters

Spring Data Repositories

For our Customer domain, we will create the following repository to load a page of Customers from the database:


public interface CustomerRepository extends JpaRepository<Customer, Long> {
    Customer findByName(String name);

The JpaRepository extends the PagingAndSortingRepository interface, which provides methods to retrieve entities using the pagination and sorting abstraction.

Spring Controller

Let's create a controller which declares the /customers end-point to render the customers. The controller method takes optional page and size query parameters to retrieve a list of customers from the database. If no parameters are available, it uses the default values.


public class CustomerController {
    private CustomerRepository customerRepository;
    public CustomerController(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    public String customersPage(HttpServletRequest request, Model model) {
        int page = 0; //default page number is 0 (yes it is weird)
        int size = 10; //default page size is 10
        if (request.getParameter("page") != null && !request.getParameter("page").isEmpty()) {
            page = Integer.parseInt(request.getParameter("page")) - 1;

        if (request.getParameter("size") != null && !request.getParameter("size").isEmpty()) {
            size = Integer.parseInt(request.getParameter("size"));
        model.addAttribute("customers", customerRepository.findAll(PageRequest.of(page, size)));
        return "customers";

Thymeleaf Template

The final step is to create a Thymeleaf template customers.html, which displays the list of customers with pagination based on a model attribute customers from our CustomerController class.

For styling, we use Bootstrap 4 pagination component. It provides utility classes to nicely display the pagination links. We will first iterate over the list to display the customers in a table. Then we will add the pagination right below the table.


<table class="table table-bordered">
        <tr th:each="customer : ${customers}">
            <td th:text="${customer.id}"></td>
            <td th:text="${customer.name}"></td>
            <td th:text="${customer.address}"></td>

<!-- customers pagination -->
<nav aria-label="Pagination" th:if="${customers.totalPages gt 0}">
    <ul class="pagination justify-content-center font-weight-bold">
        <li class="page-item" th:classappend="${customers.number eq 0} ? 'disabled'">
            <a class="page-link"
               th:href="@{/customers?page={id}(id=${customers.number lt 2 ? 1 : customers.number})}"
               aria-label="Previous" title="Previous Page" data-toggle="tooltip">
                <span aria-hidden="true">&laquo;</span>
        <li class="page-item" th:classappend="${i eq customers.number + 1} ? 'active'"
            th:each="i : ${#numbers.sequence( 1, customers.totalPages, 1)}">
            <a class="page-link" th:href="@{/customers?page={id}(id=${i})}" th:text="${i}"
               th:title="${'Page '+ i}" data-toggle="tooltip"></a>
        <li class="page-item" th:classappend="${customers.number + 1 eq customers.totalPages} ? 'disabled'">
            <a class="page-link"
               th:href="@{/customers?page={id}(id=${customers.number + 2})}"
               aria-label="Next" title="Next Page" data-toggle="tooltip">
                <span aria-hidden="true">&raquo;</span>

When you click on any page link, it will display the corresponding list of customers with the current page link highlighted.

The above code works great for displaying pagination, but there is a problem. It shows all pagination links, and if you have large number of customers, these links may be in hundreds or even thousands.

From the user experience perspective, It is inadequate to display hundreds of page links. One simple solution is only displaying 10 next page links (if available) at a time. Here is how we can do this:

<!-- customers pagination -->
<nav aria-label="Pagination" th:if="${customers.totalPages gt 0}">
    <ul class="pagination justify-content-center font-weight-medium">
        <li class="page-item" th:classappend="${customers.number eq 0} ? 'disabled'">
            <a class="page-link svg-icon"
               th:href="@{/admin/customers?page={id}(id=${customers.number lt 2 ? 1 : customers.number})}"
               title="Previous Page" rel="tooltip">
                <span aria-hidden="true" data-feather="chevrons-left" width="20" height="20"></span>
        <li class="page-item" th:classappend="${i eq customers.number + 1} ? 'active'"
            th:each="i : ${#numbers.sequence( customers.number + 1, customers.totalPages > 10 + customers.number ? customers.number + 10 : customers.totalPages, 1)}">
            <a class="page-link" th:href="@{/admin/customers?page={id}(id=${i})}" th:text="${i}"
               th:title="${'Page '+ i}"
        <li class="page-item disabled" th:if="${customers.number + 10 < customers.totalPages}">
            <a class="page-link svg-icon" href="#">
                <span data-feather="more-horizontal" width="20" height="20"></span>
        <li class="page-item" th:classappend="${customers.number + 1 eq customers.totalPages} ? 'disabled'">
            <a class="page-link svg-icon" th:href="@{/admin/customers?page={id}(id=${customers.number + 2})}"
               title="Next Page" rel="tooltip">
                <span aria-hidden="true" data-feather="chevrons-right" width="20" height="20"></span>

The above code snippet checks the number of pages left starting from the current page. If more than 10 pages are available, it renders only the next 10-page links. Otherwise, all next page links are shown. The final result will look like the following:

Spring Boot Pagination Example

✌️ Like this article? Follow me on Twitter and LinkedIn. You can also subscribe to RSS Feed.

You might also like...

Digital Ocean

The simplest cloud platform for developers & teams. Start with a $200 free credit.

Buy me a coffee ☕

If you enjoy reading my articles and want to help me out paying bills, please consider buying me a coffee ($5) or two ($10). I will be highly grateful to you ✌️

Enter the number of coffees below:

✨ Learn to build modern web applications using JavaScript and Spring Boot

I started this blog as a place to share everything I have learned in the last decade. I write about modern JavaScript, Node.js, Spring Boot, core Java, RESTful APIs, and all things web development.

The newsletter is sent every week and includes early access to clear, concise, and easy-to-follow tutorials, and other stuff I think you'd enjoy! No spam ever, unsubscribe at any time.