Table of Contents
- what-is-thymeleaf
- setup-and-dependencies
- basic-thymeleaf-syntax
- spring-boot-controller-examples
- thymeleaf-template-examples
- form-handling
- conditionals-and-loops
- layouts-and-fragments
- internationalization
What is Thymeleaf?
Thymeleaf is a modern server-side Java template engine for web and standalone environments. It's designed to be a natural template engine, meaning you can view templates as HTML files in browsers without a server.
Setup and Dependencies
1. Add Thymeleaf Dependency
Add this to your pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2. Project Structure
src/main/java/
└── com/example/demo/
├── controller/
├── model/
├── service/
└── DemoApplication.java
src/main/resources/
├── templates/
├── static/
└── application.properties
Basic Thymeleaf Syntax
Thymeleaf uses attributes starting with th:
to process templates.
Common Attributes:
-
th:text
- Sets text content -
th:utext
- Sets unescaped text content -
th:each
- Iterates over collections -
th:if
/th:unless
- Conditional rendering -
th:object
- Sets form backing object -
th:field
- Binds form fields
Spring Boot Controller Examples
1. Basic Controller
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class HomeController {
@GetMapping("/")
public String home(Model model) {
model.addAttribute("message", "Welcome to Thymeleaf!");
model.addAttribute("currentDate", java.time.LocalDate.now());
return "home";
}
@GetMapping("/greeting")
public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
2. Controller with Model Object
package com.example.demo.controller;
import com.example.demo.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.Arrays;
import java.util.List;
@Controller
public class UserController {
@GetMapping("/users")
public String showUsers(Model model) {
List<User> users = Arrays.asList(
new User(1, "John Doe", "john@example.com"),
new User(2, "Jane Smith", "jane@example.com"),
new User(3, "Bob Johnson", "bob@example.com")
);
model.addAttribute("users", users);
model.addAttribute("pageTitle", "User List");
return "users";
}
@GetMapping("/register")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new User());
return "register";
}
@PostMapping("/register")
public String registerUser(@ModelAttribute User user, Model model) {
// Save user logic would go here
model.addAttribute("message", "User registered successfully!");
return "registration-success";
}
}
3. Model Class
package com.example.demo.model;
public class User {
private Long id;
private String name;
private String email;
// Constructors
public User() {}
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
Thymeleaf Template Examples
1. Basic Template (home.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Home Page</title>
<link th:href="@{/css/style.css}" rel="stylesheet">
</head>
<body>
<header>
<h1 th:text="${message}">Default Welcome Message</h1>
</header>
<main>
<p>Today is: <span th:text="${currentDate}">2023-01-01</span></p>
<div th:if="${currentDate != null}">
<p>Date is available!</p>
</div>
<a th:href="@{/greeting(name='John')}" class="btn btn-primary">
Go to Greeting Page
</a>
</main>
<footer>
<p>© 2023 My Application</p>
</footer>
</body>
</html>
2. User List Template (users.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${pageTitle}">Users</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h1 th:text="${pageTitle}">User List</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.id}">1</td>
<td th:text="${user.name}">John Doe</td>
<td th:text="${user.email}">john@example.com</td>
<td>
<a th:href="@{'/user/' + ${user.id}}" class="btn btn-info btn-sm">View</a>
</td>
</tr>
<tr th:if="${users.empty}">
<td colspan="4" class="text-center">No users found</td>
</tr>
</tbody>
</table>
<a th:href="@{/register}" class="btn btn-success">Add New User</a>
<a th:href="@{/}" class="btn btn-secondary">Back to Home</a>
</div>
</body>
</html>
Form Handling
Registration Form (register.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>User Registration</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h1>User Registration</h1>
<form th:action="@{/register}" th:object="${user}" method="post" class="mt-4">
<div class="mb-3">
<label for="name" class="form-label">Name:</label>
<input type="text" class="form-control" id="name" th:field="*{name}" required>
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="text-danger"></div>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email:</label>
<input type="email" class="form-control" id="email" th:field="*{email}" required>
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="text-danger"></div>
</div>
<button type="submit" class="btn btn-primary">Register</button>
<a th:href="@{/users}" class="btn btn-secondary">Cancel</a>
</form>
</div>
</body>
</html>
Controller with Validation
package com.example.demo.controller;
import com.example.demo.model.User;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class RegistrationController {
@GetMapping("/register")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new User());
return "register";
}
@PostMapping("/register")
public String registerUser(@Valid @ModelAttribute("user") User user,
BindingResult result, Model model) {
if (result.hasErrors()) {
return "register";
}
// Save user logic here
model.addAttribute("message", "Registration successful!");
return "registration-success";
}
}
Conditionals and Loops
Advanced Example (advanced.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Advanced Thymeleaf</title>
</head>
<body>
<h1>Conditional Examples</h1>
<!-- Simple if condition -->
<div th:if="${user != null}">
<p>Welcome, <span th:text="${user.name}">User</span>!</p>
</div>
<div th:unless="${user != null}">
<p>Please log in.</p>
</div>
<!-- Switch case -->
<div th:switch="${user.role}">
<p th:case="'admin'">Administrator Access</p>
<p th:case="'user'">User Access</p>
<p th:case="*">Unknown Role</p>
</div>
<h1>Loop Examples</h1>
<!-- Basic loop -->
<ul>
<li th:each="item : ${items}" th:text="${item}">Item</li>
</ul>
<!-- Loop with status -->
<table>
<tr th:each="user, status : ${users}">
<td th:text="${status.index}">0</td>
<td th:text="${status.count}">1</td>
<td th:text="${user.name}">Name</td>
<td th:text="${status.odd}? 'Odd' : 'Even'">Even</td>
</tr>
</table>
</body>
</html>
Layouts and Fragments
1. Layout Template (layout.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<title layout:title-pattern="$CONTENT_TITLE - $LAYOUT_TITLE">My App</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<header th:fragment="header">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" th:href="@{/}">My App</a>
<div class="navbar-nav">
<a class="nav-link" th:href="@{/}">Home</a>
<a class="nav-link" th:href="@{/users}">Users</a>
<a class="nav-link" th:href="@{/register}">Register</a>
</div>
</div>
</nav>
</header>
<main class="container mt-4">
<div layout:fragment="content">
<!-- Content will be inserted here -->
</div>
</main>
<footer th:fragment="footer" class="bg-dark text-white text-center py-3 mt-5">
<p>© 2023 My Application. All rights reserved.</p>
</footer>
</body>
</html>
2. Page Using Layout (home-with-layout.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<title>Home Page</title>
</head>
<body>
<div layout:fragment="content">
<h1>Welcome to Our Application</h1>
<p th:text="${message}">Default welcome message</p>
<div class="alert alert-info" th:if="${currentDate != null}">
Current date: <span th:text="${currentDate}">2023-01-01</span>
</div>
</div>
</body>
</html>
Internationalization (i18n)
1. Messages Properties
messages.properties:
welcome.message=Welcome to our application!
home.title=Home Page
user.list=User List
messages_fr.properties:
welcome.message=Bienvenue dans notre application!
home.title=Page d'accueil
user.list=Liste des utilisateurs
2. Controller with i18n
@GetMapping("/international")
public String internationalPage(Model model) {
model.addAttribute("message", "welcome.message");
return "international";
}
3. Template with i18n (international.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="#{home.title}">Home</title>
</head>
<body>
<h1 th:text="#{welcome.message}">Welcome</h1>
<p th:text="#{user.list}">User List</p>
<!-- With parameters -->
<p th:text="#{welcome.user(${user.name})}">Welcome, User!</p>
<!-- Language switcher -->
<div>
<a th:href="@{/international(lang=en)}">English</a> |
<a th:href="@{/international(lang=fr)}">Français</a>
</div>
</body>
</html>
Complete Example Project Structure
src/main/java/
└── com/example/demo/
├── controller/
│ ├── HomeController.java
│ ├── UserController.java
│ └── RegistrationController.java
├── model/
│ └── User.java
├── service/
│ └── UserService.java
└── DemoApplication.java
src/main/resources/
├── templates/
│ ├── home.html
│ ├── users.html
│ ├── register.html
│ ├── layout.html
│ └── international.html
├── static/
│ ├── css/
│ │ └── style.css
│ └── js/
└── application.properties
Application Properties
# Server port
server.port=8080
# Thymeleaf configuration
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
# Internationalization
spring.messages.basename=messages
spring.web.locale-resolver=fixed
spring.web.locale=en
Running the Application
- Create a Spring Boot application with the above structure
- Add the Thymeleaf dependency
- Create the controllers and templates
- Run the application using:
./mvnw spring-boot:run
- Visit
http://localhost:8080
in your browser
Top comments (0)