In this lecture we'll focus on practical use of Thymeleaf in a Spring MVC app. We'll build dynamic web pages with Thymeleaf and learn how to generate HTML pages with data coming from our controller. Get ready — today there's going to be a lot of code, a bit of magic, and something about how the web server does its "tricks" turning requests into pretty pages.
Adding Thymeleaf to a Spring MVC project
To start using Thymeleaf, you need to add the appropriate dependency to your project. We assume you're using Maven. If your project uses Gradle, add the equivalent dependency.
Adding Thymeleaf to pom.xml
<dependencies>
<!-- Spring Boot Starter for Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>3.1.0</version> <!-- Use the current version -->
</dependency>
</dependencies>
After that Maven will download the required libraries and your project will be ready to work with Thymeleaf.
Thymeleaf configuration
Spring Boot auto-configures Thymeleaf to work with templates placed in the resources/templates folder. If you create a project on Spring Framework without Spring Boot, you'll need to set up template processing configuration yourself.
For simplicity we're using Spring Boot, so the setup is ready "out of the box".
First Thymeleaf app
Step 1: Create the controller
Let's start by creating a controller. For example, we want to display a list of students on a web page.
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Arrays;
import java.util.List;
@Controller
public class StudentController {
@GetMapping("/students")
public String getStudents(Model model) {
// Create a list of students
List<String> students = Arrays.asList("Ivan Ivanov", "Maria Petrova", "Alexey Sidorov");
// Add the list to the model
model.addAttribute("students", students);
// Return the template name (students.html)
return "students";
}
}
What happens here:
- We create an endpoint
/studentsthat handles GET requests. - Inside the method a list of students is created and passed to the
Modelobject. This object is used to send data from the controller to the template. - The method returns the template filename (
students.html). Spring MVC automatically looks for the template inresources/templates.
Step 2: Create the students.html template
Create a file students.html in resources/templates. It will contain HTML with some Thymeleaf bits.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Student List</title>
</head>
<body>
<h1>Student List</h1>
<ul>
<!-- Thymeleaf expression to display data -->
<li th:each="student : ${students}" th:text="${student}"></li>
</ul>
</body>
</html>
Explanation:
xmlns:th="http://www.thymeleaf.org"- the namespace declaration for Thymeleaf.th:each="student : ${students}"- a loop where we iterate over each student from thestudentslist.th:text="${student}"- outputs the current student into the HTML.
Step 3: Run the app
Run the Spring Boot app. Open http://localhost:8080/students in your browser. You'll see a dynamically generated page with the student list.
Working with dynamic data
Thymeleaf supports many expressions for working with data. Here are a few examples:
Thymeleaf expressions
| Syntax | Description |
|---|---|
${} |
An expression to access model variables (e.g. ${student} to access a student object) |
th:text |
Sets the text content of an element (e.g. <p> -> <p>Text</p>) |
th:href |
Sets the href attribute of a link (e.g. <a> -> <a href="URL">) |
th:if, th:unless |
Conditional rendering of HTML (e.g. th:if="${isLoggedIn}") |
Template with dynamic links
Here's an example where we add links for each student:
<ul>
<li th:each="student, iterStat : ${students}">
<a th:href="@{'/students/' + ${iterStat.index}}" th:text="${student}"></a>
</li>
</ul>
Here iterStat.index is used to add the item's index into the link.
Practice: building a UI for entities
Now let's do server-side rendering for an entity that was previously added to our app.
Step 1: Create the Book entity
package com.example.demo.model;
public class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
}
Step 2: Controller for books
package com.example.demo.controller;
import com.example.demo.model.Book;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@Controller
public class BookController {
@GetMapping("/books")
public String getBooks(Model model) {
// List of books
List<Book> books = List.of(
new Book("War and Peace", "Leo Tolstoy"),
new Book("Crime and Punishment", "Fyodor Dostoevsky"),
new Book("The Master and Margarita", "Mikhail Bulgakov")
);
model.addAttribute("books", books);
return "books";
}
}
Step 3: Template for books books.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Book List</title>
</head>
<body>
<h1>Book List</h1>
<table>
<tr>
<th>Title</th>
<th>Author</th>
</tr>
<tr th:each="book : ${books}">
<td th:text="${book.title}"></td>
<td th:text="${book.author}"></td>
</tr>
</table>
</body>
</html>
Tips and common mistakes
- Make sure the template name is correct. When a string is returned from the controller it must match the filename in the
templatesfolder. If the file is missing you'll get aTemplate not founderror. - Thymeleaf namespace. Don't forget to add
xmlns:th="http://www.thymeleaf.org"to the root HTML element. Without it Thymeleaf directives won't work. - Working with collections. If you pass a collection to the model, make sure it's not
null. Otherwise you'll get an error while iterating.
Now you've got all the tools to create server-rendered HTML pages with Thymeleaf. Use this knowledge to improve your app! 🎉
GO TO FULL VERSION