---
name: thymeleaf
description: Comprehensive Thymeleaf template engine skill for Spring Boot applications. This skill should be used when working with Thymeleaf templates, Spring Boot integration, form handling, layout management, or server-side rendering tasks. Includes best practices, code patterns, and integration examples.
---

# Thymeleaf

## Overview

This skill provides comprehensive guidance for Thymeleaf template engine integration with Spring Boot, covering template structure, form handling, layout management, security integration, and performance optimization. It enables rapid development of server-side rendered web applications with natural HTML templates.

## Core Capabilities

### 1. Template Structure and Organization

Follow the `/controller/method.html` pattern for template organization:

```
src/main/resources/templates/
├── layout/
│   ├── base.html
│   └── fragments/
│       ├── header.html
│       ├── footer.html
│       └── forms.html
├── user/
│   ├── profile.html
│   ├── edit.html
│   └── list.html
└── admin/
    ├── dashboard.html
    └── settings.html
```

**Best Practices:**

- Use descriptive, lowercase names
- Group related templates in directories
- Keep fragments separate for reusability
- Follow Spring MVC controller mapping conventions

### 2. Essential Thymeleaf Expressions

#### Variable Expressions `${...}`

```html
<!-- Basic variable display -->
<span th:text="${user.name}">Default Name</span>

<!-- Property navigation -->
<span th:text="${user.address.city}">Default City</span>

<!-- Method calls -->
<span th:text="${user.getFullName()}">Full Name</span>
```

#### Message Expressions `#{...}`

```html
<!-- Internationalization -->
<h1 th:text="#{welcome.message}">Welcome!</h1>
<button th:text="#{form.submit}">Submit</button>
```

#### Link Expressions `@{...}`

```html
<!-- URL generation -->
<a th:href="@{/users/{id}(id=${user.id})}">User Profile</a>
<form th:action="@{/login}" method="post">
  <link th:href="@{/css/style.css}" rel="stylesheet" />
</form>
```

#### Fragment Expressions `~{...}`

```html
<!-- Template inclusion -->
<div th:replace="~{layout/base :: content}">Content</div>
<div th:insert="~{fragments/header :: header}">Header</div>
```

### 3. Standard Attributes Reference

#### Text and Content

```html
<!-- Text output (escaped) -->
<p th:text="${message}">Default message</p>

<!-- Unescaped text (for HTML content) -->
<div th:utext="${htmlContent}">Default content</div>
```

#### Conditional Rendering

```html
<!-- If conditions -->
<div th:if="${user.isAdmin()}">Admin content</div>
<div th:unless="${user.isGuest()}">Authenticated content</div>

<!-- Switch statements -->
<div th:switch="${user.role}">
  <p th:case="'ADMIN'">Administrator</p>
  <p th:case="'USER'">Regular User</p>
  <p th:case="*">Unknown Role</p>
</div>
```

#### Iteration

```html
<!-- Loop through collections -->
<tr th:each="item : ${items}">
  <td th:text="${item.name}">Item Name</td>
  <td th:text="${item.price}">0.00</td>
</tr>

<!-- With iteration status -->
<li th:each="user,iterStat : ${users}">
  <span th:text="${iterStat.count}">1</span>.
  <span th:text="${user.name}">User Name</span>
  <span th:if="${iterStat.last}"> (Last)</span>
</li>
```

### 4. Form Handling with Spring Integration

#### Form Setup

```html
<form th:action="@{/users/save}" th:object="${user}" method="post">
  <input type="text" th:field="*{name}" />
  <input type="email" th:field="*{email}" />
  <input type="password" th:field="*{password}" />

  <select th:field="*{role}">
    <option th:each="role : ${roles}" th:value="${role}" th:text="${role}">
      Role
    </option>
  </select>

  <input type="checkbox" th:field="*{active}" />
  <button type="submit">Save</button>
</form>
```

#### Validation Error Display

```html
<div th:if="${#fields.hasErrors('name')}" class="error">
  <span th:each="err : ${#fields.errors('name')}" th:text="${err}"
    >Error message</span
  >
</div>

<!-- Global errors -->
<div th:if="${#fields.hasGlobalErrors()}" class="alert alert-danger">
  <span th:each="err : ${#fields.globalErrors()}" th:text="${err}"
    >Global error</span
  >
</div>
```

### 5. Layout and Fragment Management

#### Base Layout Template

```html
<!-- layout/base.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head th:fragment="head">
    <meta charset="UTF-8" />
    <title th:text="${title}">Default Title</title>
    <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet" />
  </head>
  <body>
    <header th:replace="~{fragments/header :: header}">Header</header>

    <main>
      <div th:replace="~{::content}">Content</div>
    </main>

    <footer th:replace="~{fragments/footer :: footer}">Footer</footer>

    <script th:src="@{/js/bootstrap.bundle.min.js}"></script>
  </body>
</html>
```

#### Fragment Definitions

```html
<!-- fragments/header.html -->
<header th:fragment="header" class="navbar">
  <nav>
    <a th:href="@{/}" th:text="#{nav.home}">Home</a>
    <a th:href="@{/users}" th:text="#{nav.users}">Users</a>
    <div th:replace="~{fragments/auth :: authMenu}">Auth Menu</div>
  </nav>
</header>

<!-- fragments/forms.html -->
<div th:fragment="inputRow(object, field, type, required)">
  <div class="form-group">
    <label th:for="${field}" th:text="#{form.${field}}">Label</label>
    <input
      th:field="*{${field}}"
      th:type="${type ?: 'text'}"
      th:class="${required ? 'form-control required' : 'form-control'}"
    />
    <div th:if="${#fields.hasErrors(field)}" class="error-message">
      <span th:each="err : ${#fields.errors(field)}" th:text="${err}"></span>
    </div>
  </div>
</div>
```

#### Content Template Usage

```html
<!-- user/profile.html -->
<html
  xmlns:th="http://www.thymeleaf.org"
  th:replace="~{layout/base :: layout(~{::content})}"
  th:with="title=#{user.profile}"
>
  <section th:fragment="content">
    <h1 th:text="#{user.profile}">User Profile</h1>

    <form th:action="@{/user/profile}" th:object="${user}" method="post">
      <div
        th:replace="~{fragments/forms :: inputRow('name', 'text', true)}"
      ></div>
      <div
        th:replace="~{fragments/forms :: inputRow('email', 'email', true)}"
      ></div>
      <div
        th:replace="~{fragments/forms :: inputRow('phone', 'tel', false)}"
      ></div>

      <button type="submit" class="btn btn-primary" th:text="#{form.save}">
        Save
      </button>
    </form>
  </section>
</html>
```

### 6. Spring Security Integration

#### Dependency Setup

```xml
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
```

#### Security Conditional Content

```html
<!-- Namespace declaration -->
<html
  xmlns:th="http://www.thymeleaf.org"
  xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
>
  <!-- Authentication-based content -->
  <div sec:authorize="isAuthenticated()">
    <span th:text="${#authentication.name}">User</span>
    <a th:href="@{/logout}" th:text="#{nav.logout}">Logout</a>
  </div>

  <div sec:authorize="!isAuthenticated()">
    <a th:href="@{/login}" th:text="#{nav.login}">Login</a>
  </div>

  <!-- Role-based content -->
  <div sec:authorize="hasRole('ADMIN')">
    <a th:href="@{/admin/dashboard}">Admin Dashboard</a>
  </div>

  <!-- URL-based authorization -->
  <a
    sec:authorize="hasAuthority('USER_READ')"
    th:href="@{/users/{id}(id=${user.id})}"
  >
    View User Details
  </a>
</html>
```

### 7. Utility Objects and Expressions

#### Dates and Times

```html
<!-- Formatting dates -->
<span th:text="${#dates.format(user.birthDate, 'yyyy-MM-dd')}">2024-01-01</span>
<span th:text="${#dates.format(timestamp, 'MMM dd, yyyy HH:mm')}"
  >Jan 01, 2024 12:00</span
>

<!-- Date arithmetic -->
<span th:text="${#dates.month(#dates.createNow())}">1</span>
<span th:if="${#dates.isAfter(user.lastLogin, #dates.createNow())}">
  User logged in today
</span>
```

#### Strings

```html
<!-- String manipulation -->
<span th:text="${#strings.toUpperCase(user.name)}">NAME</span>
<span th:text="${#strings.substring(user.email, 0, 3) + '***'}">***</span>
<span th:text="${#strings.length(message)}">0</span>

<!-- String checks -->
<div th:if="${#strings.isEmpty(user.phone)}" class="warning">
  No phone number provided
</div>
<span th:if="${#strings.contains(user.name, 'admin')}" class="admin-badge"
  >ADMIN</span
>
```

#### Numbers

```html
<!-- Number formatting -->
<span th:text="${#numbers.formatDecimal(price, 2, 2)}">0.00</span>
<span th:text="${#numbers.formatPercent(discount, 1, 1)}">0%</span>

<!-- Number sequences -->
<div th:each="i : ${#numbers.sequence(0, 4)}">
  <span th:text="${i}">0</span>
</div>
```

#### Lists and Sets

```html
<!-- List operations -->
<span th:text="${#lists.size(users)}">0</span>
<div th:if="${#lists.isEmpty(items)}" class="empty-state">No items found</div>

<!-- Set operations -->
<span th:if="${#sets.contains(user.roles, 'ADMIN')}" class="admin"
  >Admin User</span
>
```

### 8. Internationalization (i18n)

#### Message Properties Setup

```properties
# messages.properties
welcome.message=Welcome to our application!
user.profile=User Profile
form.save=Save Changes
nav.home=Home
nav.users=Users

# messages_fr.properties
welcome.message=Bienvenue dans notre application!
user.profile=Profil de l'utilisateur
form.save=Enregistrer les modifications
```

#### Locale-Specific Content

```html
<!-- Language switcher -->
<div class="language-selector">
  <a
    th:href="@{''(lang=en)}"
    th:class="${#locale.language == 'en' ? 'active' : ''}"
    >English</a
  >
  <a
    th:href="@{''(lang=fr)}"
    th:class="${#locale.language == 'fr' ? 'active' : ''}"
    >Français</a
  >
</div>

<!-- Parameterized messages -->
<span th:text="#{welcome.user(${user.name})}">Welcome User!</span>
<span th:text="#{item.count(${#lists.size(items)})}">0 items</span>
```

### 9. Performance and Caching

#### Development Configuration

```properties
# application.properties (development)
spring.thymeleaf.cache=false
spring.thymeleaf.template-resolver-order=classpath
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
```

#### Production Configuration

```properties
# application.properties (production)
spring.thymeleaf.cache=true
spring.thymeleaf.template-resolver-order=classpath
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
```

#### Hot Reload Setup (Development)

```java
@Configuration
@Profile("dev")
public class ThymeleafDevConfig {

    @Bean
    public ITemplateResolver templateResolver() {
        FileTemplateResolver resolver = new FileTemplateResolver();
        resolver.setPrefix("src/main/resources/templates/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode(TemplateMode.HTML);
        resolver.setCacheable(false);
        resolver.setCheckExistence(true);
        return resolver;
    }
}
```

### 10. Common Patterns and Examples

#### CRUD Operations Template

```html
<!-- List view -->
<table class="table">
  <thead>
    <tr>
      <th th:text="#{user.name}">Name</th>
      <th th:text="#{user.email}">Email</th>
      <th th:text="#{user.actions}">Actions</th>
    </tr>
  </thead>
  <tbody>
    <tr th:each="user : ${users}">
      <td th:text="${user.name}">User Name</td>
      <td th:text="${user.email}">user@example.com</td>
      <td>
        <a th:href="@{/users/{id}(id=${user.id})}" th:text="#{action.view}"
          >View</a
        >
        <a th:href="@{/users/{id}/edit(id=${user.id})}" th:text="#{action.edit}"
          >Edit</a
        >
        <form
          th:action="@{/users/{id}/delete(id=${user.id})}"
          method="post"
          style="display:inline;"
        >
          <button type="submit" th:text="#{action.delete}">Delete</button>
        </form>
      </td>
    </tr>
  </tbody>
</table>

<!-- Empty state -->
<div th:if="${#lists.isEmpty(users)}" class="empty-state">
  <p th:text="#{user.empty}">No users found.</p>
  <a th:href="@{/users/new}" th:text="#{user.create}">Create User</a>
</div>
```

#### Pagination

```html
<!-- Pagination controls -->
<nav th:if="${page.totalPages > 1}" class="pagination">
  <a
    th:href="@{''(page=1)}"
    th:class="${page.number == 0} ? 'disabled' : ''"
    th:text="#{pagination.first}"
    >First</a
  >

  <a
    th:href="@{''(page=${page.number})}"
    th:class="${page.number == 0} ? 'disabled' : ''"
    th:text="#{pagination.previous}"
    >Previous</a
  >

  <span th:text="|${page.number + 1} / ${page.totalPages}|">1 / 1</span>

  <a
    th:href="@{''(page=${page.number + 2})}"
    th:class="${page.number + 1 >= page.totalPages} ? 'disabled' : ''"
    th:text="#{pagination.next}"
    >Next</a
  >

  <a
    th:href="@{''(page=${page.totalPages})}"
    th:class="${page.number + 1 == page.totalPages} ? 'disabled' : ''"
    th:text="#{pagination.last}"
    >Last</a
  >
</nav>
```

#### Search and Filtering

```html
<!-- Search form -->
<form th:action="@{/users}" method="get" class="search-form">
  <input
    type="text"
    name="search"
    th:value="${param.search}"
    th:placeholder="#{search.placeholder}"
    class="form-control"
  />
  <select name="role" th:value="${param.role}" class="form-control">
    <option value="" th:text="#{search.all.roles}">All Roles</option>
    <option
      th:each="role : ${availableRoles}"
      th:value="${role}"
      th:text="${role}"
      th:selected="${role == param.role}"
    >
      Role
    </option>
  </select>
  <button type="submit" th:text="#{search.submit}">Search</button>
</form>

<!-- Results count -->
<div th:if="${param.search or param.role}" class="search-results">
  <span th:text="#{search.results(${#lists.size(users)})">0 results</span>
  <a th:href="@{/users}" th:text="#{search.clear}">Clear</a>
</div>
```

## Resources

This skill includes organized resource directories for Thymeleaf development:

### scripts/

Contains Python scripts for Thymeleaf template processing and validation:

- `template_validator.py` - Validates Thymeleaf template syntax
- `fragment_generator.py` - Generates reusable fragment templates
- `i18n_extractor.py` - Extracts message keys for internationalization

### references/

Comprehensive documentation and references:

- `attribute_reference.md` - Complete Thymeleaf attribute reference
- `spring_integration.md` - Spring Boot integration patterns
- `best_practices.md` - Performance and security guidelines
- `bootstrap_examples.md` - Bootstrap integration examples

### assets/

Template files and boilerplate code:

- `layout/` - Base layout templates
- `fragments/` - Reusable fragment components
- `examples/` - Complete working examples

---

**Usage Guidelines:**

1. Start with template structure setup for proper organization
2. Use fragments for reusable components to maintain DRY principles
3. Implement proper form handling with validation
4. Add security layers using Spring Security integration
5. Optimize for production with proper caching configuration
6. Test templates both statically and dynamically
