September 3, 2026

Part 2 – How to Implement Pagination in Spring Boot with Thymeleaf

Learn how to implement pagination in Spring Boot with Thymeleaf, add page navigation, and combine pagination with sorting using Spring Data JPA.

TL;DR

  • Add First, Previous, Next, and Last page links to a Thymeleaf table.

  • Generate page number links dynamically using Thymeleaf.

  • Use the current page and total pages to control pagination navigation.

  • Combine pagination with sorting so users can sort records on the current page.

  • Update the service and controller to use Pageable with sorting and pagination.

We would not complete this series on How to Implement Pagination in Spring Boot. You can find Part 1 here.

In this part we would add the Page Number links to the table footer. We would also add the links for the First, Previous, Next and Last pages.

 

1. The Pagination Layout

We would use the pagination layout available in Bootstrap. This is shown below:

<nav aria-label="Page navigation example">
    Total Items [[${totalItems}]] : Page [[${currentPage}]] of [[${totalPages}]]
    
    <ul class="pagination">
        <li class="page-item">
            First
        </li>

        <li class="page-item">
            Previous
        </li>

        <li class="page-item">
             1 2 3 4 ...
        </li>

        <li class="page-item">
            Next
        </li>

        <li class="page-item">
            Last
        </li>
    </ul>
</nav>

 

2. First Page

This would be a link if  the current page is greater than 1. In other words, it would be a plain text unless the current page is greater than 1. It makes a request for page number of 1. The code is given below:

<a  class="page-link" th:if="${currentPage > 1}" th:href="@{/parameters/countries/page/1}">First</a>
<span th:unless="${currentPage > 1}">First</span>

 

3. Previous Page

Just like the first page, the text would be a link if the current page is greater than 1. However, it makes a request for page number of currentPage – 1. See the code below:

<a th:if="${currentPage > 1}" th:href="@{'/parameters/countries/page/' + ${currentPage - 1}}">Previous</a>
<span th:unless="${currentPage > 1}">Previous</span>

 

4. Page Number Links

The page number links would exactly as we have it before but now inside the <li></li> tags.

<span th:each="i: ${#numbers.sequence(1, totalPages)}">
        <a th:if="${i != currentPage}" th:href="@{'/parameters/countries/page/' + ${i}}">[[${i}]]</a>
        <span th:unless="${i != currentPage}">[[${i}]]</span>
</span>

 

5. Next Page

In case of the next page, the Next displays a link if the current page is less than the total pages. Otherwise, it displays a plain text. It request for currentPage + 1

<a th:if="${currentPage < totalPages}" th:href="@{'/parameters/countries/page/' + ${currentPage + 1}}">Next</a>
<span th:unless="${currentPage < totalPages}">Next</span>

 

6. Last Page

In this case, a link would be displayed if the current page is less than the total pages. Otherwise, it displays a plain text. It simply request for the page number of totalPages.

<a th:if="${currentPage < totalPages}" th:href="@{'/parameters/countries/page/' + ${totalPages}}">Last</a>
<span th:unless="${currentPage < totalPages}">Last</span>

 

7. Combining Paging and Sorting

Now, if you we have the pagination working as expected. But if you try to do sorting by clicking on the column headers, it doesn’t work. We’ll fix this by combining pagination and sorting.

First, note the following points:

  • pagination takes precedence over sorting. So we’ll focus on making sorting work
  • to sort, we’ll need to provide the page number to sort. This can be done via path variable
  • paging to another page could undo existing sort status

You have to make the following changes:

Change 1 – In the html template, adjust the head links to includes the page to sort. In this case, it would be the current page like so:

<a th:href="@{'/parameters/countries/page/' + ${currentPage} + '/description?sortDir=' + ${reverseSortDir}}">

 

Change 2 – In the controller, create a new method getPageWithSort() that derives from getAllWithSort()

Change 3 – The GetMapping for this method would be:

@GetMapping("/parameters/page/{pageNumber}/countries/{field}")

 

Change 4 – Add the pageNumber as parameter to the method. Name it currentPage

Change 5 – Add the currentPage as third argument to the findCountryWithSorting() function call. This means the findAll() method would then take pageable as argument instead of sort. The modified findAllWithSort() is given below:

public Page<Country> findCountryWithSorting2(String field, String direction, int pageNumber){
    Sort sort = direction.equalsIgnoreCase(Sort.Direction.ASC.name())?
            Sort.by(field).ascending(): Sort.by(field).descending();
    Pageable pageable = PageRequest.of(pageNumber - 1,5, sort);
    return countryRepository.findAll(pageable);
}

 

8. Modify the Controller Method

Currently, in the controller, we have the getAllWithSort() function that takes two parameters: sortField and sortDirection. Now we would do three things:

  • change the name of the function from getAllWithSort() to getPageWithSort()
  • make the function accept one more parameter, that is the page number to get
  • pass the page number across as well to the findAllWithSort() which is in the service.

The new getPageWithSort() is given below:

@GetMapping("/parameters/countries/page/{pageNumber}/{field}")
public String getPageWithSort(Model model,
                              @PathVariable("pageNumber") int currentPage,
                              @PathVariable String field,
                              @PathParam("sortDir") String sortDir){

    Page<Country> page = countryService.findCountryWithSorting2(field, sortDir, currentPage);
    List<Country> countries = page.getContent();
    int totalPages = page.getTotalPages();
    long totalItems = page.getTotalElements();

    model.addAttribute("currentPage", currentPage);
    model.addAttribute("totalPages", totalPages);
    model.addAttribute("totalItems", totalItems);

    model.addAttribute("sortDir", sortDir);
    model.addAttribute("reverseSortDir", sortDir.equals("asc")?"desc":"asc");
    model.addAttribute("countries", countries);
    return "/parameters/countries";
}

 

I do recommend that you watch the video tutorial here for clarification.

FAQs

How do I implement pagination in Spring Boot with Thymeleaf?

You can use Spring Data’s Page and Pageable features to retrieve records page by page, then use Thymeleaf to display navigation links for the available pages.

How do I add First and Previous page links in Thymeleaf?

Use Thymeleaf conditions to display links only when the current page is greater than the first page. The First link points to page 1, while the Previous link points to the current page minus 1.

How do I create page number links with Thymeleaf?

You can use Thymeleaf’s #numbers.sequence() to generate a sequence from 1 to the total number of pages and create a link for each page.

How do I combine pagination and sorting in Spring Boot?

Pass both the page number and sorting information to the controller, then create a Pageable object with PageRequest.of() and a Sort object before calling the repository’s findAll(pageable) method.

How do I implement Next and Last page navigation?

The Next link should point to the current page plus one when the current page is less than the total number of pages. The Last link points directly to the value of totalPages.

Final Thoughts

Pagination makes it easier to display large amounts of data without loading every record onto a single page. With Thymeleaf, you can provide users with familiar navigation controls such as First, Previous, page numbers, Next, and Last.

The tutorial also shows how to combine pagination with sorting. By passing the current page and sort direction to the controller and using Spring Data’s Pageable, you can allow users to navigate through paginated results while maintaining sorting functionality.

Kindson Munonye

Kindson Munonye is a software engineer and technical author covering machine learning, statistics, REST APIs, Python, and software engineering. He publishes free tutorials on The Genius Blog and live classes on Alkademy. GitHub · LinkedIn · About · Alkademy

View all posts by Kindson Munonye →
0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted