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)