In this blog, we will learn how to create pagination in Spring Boot Application using Thymeleaf. Pagination is very important if we have many instances of dataset and get data in chunks. Pagination helps to get data according to range of sequence. So lets implement pagination in Spring Boot using Thymeleaf and Bootstrap Pagination code.

Initial Project Setup

Let’s say, we have a Product model

@Entity 
public class Product {
    
    @Id
    private long id;
    private String name;
    private double price; 

    // constructors, getters and setters 

}

And we have a repository

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    Product findAllByName(String name);
}

With a controller, which displays all products using findAllByName() as name filter.

@Autowired
ProductRepository productRepository;

@GetMapping("/products")
public String productListing(Model model){
    model.addAttribute("products", productRepository.findAllByName("Fan Motor"));
    return "product-listing";
}

product-listing.html

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>

<div class="container mt-3">
  <h2>Basic Table</h2>
  <p>The .table class adds basic styling (light padding and horizontal dividers) to a table:</p>            
  <table class="table">
    <thead>
      <tr>
        <th>id</th>
        <th>name</th>
        <th>price</th>
      </tr>
    </thead>
    <tbody>
      <tr th:each="obj : ${products}">
        <td>[[${obj.id}]]</td>
        <td>[[${obj.name}]]</td>
        <td>[[${obj.price}]]</td>
      </tr>
    </tbody>
  </table>
</div>

// pagination will be added here

</body>
</html>

Till above, we were able to display all products. But now we want to add pagination, so lets see how to do it.

Pagination in Spring Boot

First of all, we need to add Page instance in ProductRepository on findAllByName

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    Page<Product> findAllByName(String name, Pageable pageable);
}

Once repository returns Page Object, we need to edit in controller. For pagination, we need page number and page size parameter.

@Autowired
ProductRepository productRepository;

@GetMapping("/products")
public String productListing(Model model,
           @RequestParam("page") Optional<Integer> page,
           @RequestParam("size") Optional<Integer> size
){
    int currentPage = page.orElse(1);
    int pageSize = size.orElse(16);
    Pageable pageable = PageRequest.of(currentPage - 1, pageSize);
    Page<User> pageObj = productRepository.findAllByName("Fan Motor", pageable);
    int totalPages = pageObj.getTotalPages();
    long totalItems = pageObj.getTotalElements();
    List<User> productList = pageObj.getContent();

    model.addAttribute("currentPage", 1);
    model.addAttribute("totalPages", totalPages);
    model.addAttribute("totalItems", totalItems);
    model.addAttribute("products", productList);

    return "product-listing";
}

Now that our controller is returning currentPage, totalPages, totalItems instance, we can use these to do changes and display pagination in html.

Now will add pagination code in product-listing.html

<nav>
  <ul class="pagination justify-content-center">
    <li class="page-item" th:if="${currentPage > totalPages}">
      <a class="page-link text-dark" th:href="@{'/dashboard?page=' + ${currentPage-1}} + '&size=16'" aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
        <span class="sr-only">Previous</span>
      </a>
    </li>
    <li class="page-item" th:each="i: ${#numbers.sequence(1, totalPages)}">
      <a class="page-link text-dark" th:href="@{'/dashboard?page=' + ${i}} + '&size=16'">[[${i}]]</a>
    </li>
    <li class="page-item" th:if="${currentPage < totalPages}">
      <a class="page-link text-dark" th:href="@{'/dashboard?page=' + ${currentPage+1}} + '&size=16'" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
        <span class="sr-only">Next</span>
      </a>
    </li>
  </ul>
</nav>

Please add above code, at suitable place. Please note, you can change page size, in above code, we have kept it as 16.

Lets break above code to see different section in pagination.

Previous page in Pagination

<li class="page-item" th:if="${currentPage > totalPages}">
      <a class="page-link text-dark" th:href="@{'/dashboard?page=' + ${currentPage-1}} + '&size=16'" aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
        <span class="sr-only">Previous</span>
      </a>
</li>

Next Page in Pagination

<li class="page-item" th:if="${currentPage < totalPages}">
      <a class="page-link text-dark" th:href="@{'/dashboard?page=' + ${currentPage+1}} + '&size=16'" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
        <span class="sr-only">Next</span>
      </a>
</li>

Pagination Implement using Thymeleaf Fragment

I personally like to create a pagination fragment by passing all required parameters and call to replace code at required place.

// product-listing.html
<div th:replace="users/fragments/pagination.html:: page_fragment(totalPages=${totalPages}, currentPage=${currentPage}, totalItems=${totalItems})">
</div>
// pagination-fragment.html
<nav th:fragment="page_fragment(totalPages, currentPage, totalItems)">
  <ul class="pagination justify-content-center">
    <li class="page-item" th:if="${currentPage > totalPages}">
      <a class="page-link text-dark" th:href="@{'/dashboard?page=' + ${currentPage-1}} + '&size=16'" aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
        <span class="sr-only">Previous</span>
      </a>
    </li>
    <li class="page-item" th:each="i: ${#numbers.sequence(1, totalPages)}">
      <a class="page-link text-dark" th:href="@{'/dashboard?page=' + ${i}} + '&size=16'">[[${i}]]</a>
    </li>
    <li class="page-item" th:if="${currentPage < totalPages}">
      <a class="page-link text-dark" th:href="@{'/dashboard?page=' + ${currentPage+1}} + '&size=16'" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
        <span class="sr-only">Next</span>
      </a>
    </li>
  </ul>
</nav>