1. Overview

In this quick tutorial, we’ll build a simple application to display a list of items with pagination using Spring and Thymeleaf.

For an introduction to integrating Thymeleaf with Spring, please look at our article here.

2. Maven Dependencies

Besides the usual Spring dependencies, we’ll add the dependencies for Thymeleaf and Spring Data Commons:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-commons</artifactId>
    <version>2.3.2.RELEASE</version>
</dependency>

We can find the latest thymeleaf-spring5 and spring-data-commons dependencies in the Maven Central repository.

3. Models

Our sample application will demonstrate pagination for a list of books.

First, let’s define a Book class with two fields and an all-arguments constructor:

public class Book {
    private int id;
    private String name;

    // standard constructor, setters and getters
}

4. Service

Then we’ll create a service to generate the paginated book list for the requested page using the Spring Data Commons library:

@Service
public class BookService {

    final private List<Book> books = BookUtils.buildBooks();

    public Page<Book> findPaginated(Pageable pageable) {
        int pageSize = pageable.getPageSize();
        int currentPage = pageable.getPageNumber();
        int startItem = currentPage * pageSize;
        List<Book> list;

        if (books.size() < startItem) {
            list = Collections.emptyList();
        } else {
            int toIndex = Math.min(startItem + pageSize, books.size());
            list = books.subList(startItem, toIndex);
        }

        Page<Book> bookPage
          = new PageImpl<Book>(list, PageRequest.of(currentPage, pageSize), books.size());

        return bookPage;
    }
}

In the above service, we created a method to return the selected Page based on the requested page, represented by the Pageable interface. The PageImpl class helps to filter out the paginated list of books.

5. Spring Controller

We’ll need a Spring controller to retrieve the book list of a selected page when given the page size and current page number.

To use the default values for the selected page and page size, we can access the resource at /listBooks, without any parameters.

We can add parameters for page and size if any page size or specific page is required.

For example, /listBooks?page=2&size=6 will retrieve page two with six items per page:

@Controller
public class BookController {

    @Autowired
    private BookService bookService;

    @RequestMapping(value = "/listBooks", method = RequestMethod.GET)
    public String listBooks(
      Model model, 
      @RequestParam("page") Optional<Integer> page, 
      @RequestParam("size") Optional<Integer> size) {
        int currentPage = page.orElse(1);
        int pageSize = size.orElse(5);

        Page<Book> bookPage = bookService.findPaginated(PageRequest.of(currentPage - 1, pageSize));

        model.addAttribute("bookPage", bookPage);

        int totalPages = bookPage.getTotalPages();
        if (totalPages > 0) {
            List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
                .boxed()
                .collect(Collectors.toList());
            model.addAttribute("pageNumbers", pageNumbers);
        }

        return "listBooks.html";
    }
}

To prepare our pagination for the view, we’ve added model attributes in the Spring controller, including the selected Page and a list of page numbers.

6. Thymeleaf Template

Now it’s time to create a Thymeleaf template, “listBooks.html”, which displays the list of books with pagination based on model attributes from our Spring controller.

First, we iterate the list of books and display them in a table. Then we show the pagination when the total number of pages exceeds zero.

Every time we click and select a page, the corresponding list of books will be displayed with the current page link highlighted:

<table border="1">
    <thead>
        <tr>
            <th th:text="#{msg.id}" />
            <th th:text="#{msg.name}" />
        </tr>
    </thead>
    <tbody>
        <tr th:each="book, iStat : ${bookPage.content}"
            th:style="${iStat.odd}? 'font-weight: bold;'"
            th:alt-title="${iStat.even}? 'even' : 'odd'">
            <td th:text="${book.id}" />
            <td th:text="${book.name}" />
        </tr>
    </tbody>
</table>
<div th:if="${bookPage.totalPages > 0}" class="pagination"
    th:each="pageNumber : ${pageNumbers}">
    <a th:href="@{/listBooks(size=${bookPage.size}, page=${pageNumber})}"
        th:text=${pageNumber}
        th:class="${pageNumber==bookPage.number + 1} ? active"></a>
</div>

7. Conclusion

In this article, we demonstrated how to paginate a list using Thymeleaf with the Spring framework.

All the code samples used in the article are available on GitHub.