DEV Community

Sadiul Hakim
Sadiul Hakim

Posted on

Complete Beginner's Guide to Thymeleaf with Spring Boot

Table of Contents

  1. what-is-thymeleaf
  2. setup-and-dependencies
  3. basic-thymeleaf-syntax
  4. spring-boot-controller-examples
  5. thymeleaf-template-examples
  6. form-handling
  7. conditionals-and-loops
  8. layouts-and-fragments
  9. 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> 
Enter fullscreen mode Exit fullscreen mode

2. Project Structure

src/main/java/ └── com/example/demo/ ├── controller/ ├── model/ ├── service/ └── DemoApplication.java src/main/resources/ ├── templates/ ├── static/ └── application.properties 
Enter fullscreen mode Exit fullscreen mode

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"; } } 
Enter fullscreen mode Exit fullscreen mode

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"; } } 
Enter fullscreen mode Exit fullscreen mode

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; } } 
Enter fullscreen mode Exit fullscreen mode

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>&copy; 2023 My Application</p> </footer> </body> </html> 
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

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"; } } 
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

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>&copy; 2023 My Application. All rights reserved.</p> </footer> </body> </html> 
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

Internationalization (i18n)

1. Messages Properties

messages.properties:

welcome.message=Welcome to our application! home.title=Home Page user.list=User List 
Enter fullscreen mode Exit fullscreen mode

messages_fr.properties:

welcome.message=Bienvenue dans notre application! home.title=Page d'accueil user.list=Liste des utilisateurs 
Enter fullscreen mode Exit fullscreen mode

2. Controller with i18n

@GetMapping("/international") public String internationalPage(Model model) { model.addAttribute("message", "welcome.message"); return "international"; } 
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Running the Application

  1. Create a Spring Boot application with the above structure
  2. Add the Thymeleaf dependency
  3. Create the controllers and templates
  4. Run the application using:
 ./mvnw spring-boot:run 
Enter fullscreen mode Exit fullscreen mode
  1. Visit http://localhost:8080 in your browser

Top comments (0)