# Thymeleaf Best Practices Guide

## Template Structure Best Practices

### Directory Organization

```
src/main/resources/templates/
├── layout/              # Base layouts and main structure
│   ├── base.html         # Main layout template
│   ├── admin.html        # Admin-specific layout
│   └── public.html       # Public-facing layout
├── fragments/            # Reusable components
│   ├── forms/           # Form fragments
│   ├── tables/           # Table fragments
│   ├── cards/            # Card components
│   └── navigation/       # Navigation components
├── errors/              # Error pages
│   ├── 404.html
│   ├── 500.html
│   └── default.html
└── features/            # Feature-specific templates
    ├── user/
    ├── admin/
    └── public/
```

### Naming Conventions

- **Templates**: lowercase, hyphen-separated (`user-profile.html`)
- **Fragments**: descriptive, purpose-based (`user-form.html`)
- **Layouts**: functional naming (`admin-layout.html`)
- **Directories**: plural, descriptive (`users/`, `settings/`)

## Performance Optimization

### Caching Strategies

```properties
# Development - Disable cache for hot reload
spring.thymeleaf.cache=false
spring.thymeleaf.check-template=true

# Production - Enable caching
spring.thymeleaf.cache=true
spring.thymeleaf.cache-ttl-ms=3600000
```

### Template Efficiency

```html
<!-- ✅ Good: Efficient iteration with status -->
<tbody>
  <tr
    th:each="user,stat : ${users}"
    th:class="${stat.odd} ? 'odd-row' : 'even-row'"
  >
    <td th:text="${stat.count}">1</td>
    <td th:text="${user.name}">Name</td>
  </tr>
</tbody>

<!-- ❌ Avoid: Complex nested conditions -->
<div
  th:if="${user != null and user.active and user.role == 'ADMIN' and user.lastLogin != null}"
>
  <!-- Content -->
</div>

<!-- ✅ Better: Extract to utility method -->
<div th:if="${#userUtils.isActiveAdmin(user)}">
  <!-- Content -->
</div>
```

### Fragment Reuse

```html
<!-- ✅ Create reusable fragments -->
<div th:fragment="userCard(user)" class="user-card">
  <img
    th:src="@{/images/users/{id}.jpg(id=${user.id})}"
    th:alt="${user.name}"
    class="user-avatar"
  />
  <h3 th:text="${user.name}">User Name</h3>
  <p th:text="${user.email}">email@example.com</p>
  <div th:replace="~{fragments/badges :: userRole(user.role)}"></div>
</div>

<!-- ✅ Use fragments consistently -->
<div th:each="user : ${users}">
  <div th:replace="~{fragments/user :: userCard(user=${user})}"></div>
</div>
```

## Security Best Practices

### Input Validation and Escaping

```html
<!-- ✅ Always escape user input -->
<p th:text="${userComment}">User comment</p>

<!-- ❌ Never use unescaped for user input -->
<p th:utext="${userComment}">Potentially dangerous</p>

<!-- ✅ Use utext only for trusted content -->
<div th:utext="${trustedHtmlContent}">Safe HTML</div>
```

### CSRF Protection

```html
<!-- ✅ Include CSRF token in forms -->
<form th:action="@{/users/save}" method="post">
  <input
    type="hidden"
    th:name="${_csrf.parameterName}"
    th:value="${_csrf.token}"
  />
  <!-- Form fields -->
</form>

<!-- ✅ Use Spring Security form tag -->
<form th:action="@{/users/save}" th:object="${user}" method="post">
  <!-- CSRF automatically included -->
</form>
```

### Authorization

```html
<!-- ✅ Role-based content display -->
<div sec:authorize="hasRole('ADMIN')">
  <button th:href="@{/admin/panel}" class="admin-btn">Admin Panel</button>
</div>

<!-- ✅ Method-level security -->
<button
  sec:authorize="hasAuthority('USER_WRITE')"
  th:href="@{/users/{id}/edit(id=${user.id})}"
>
  Edit User
</button>

<!-- ✅ Ownership-based access -->
<div
  th:if="${#authentication.name == user.owner}"
  sec:authorize="hasAuthority('USER_EDIT')"
>
  <button th:href="@{/posts/{id}/edit(id=${post.id})}">Edit Post</button>
</div>
```

## Code Quality and Maintainability

### Template Readability

```html
<!-- ✅ Clear structure with comments -->
<!-- User Profile Section -->
<section class="user-profile">
  <header class="profile-header">
    <h1 th:text="#{user.profile.title}">User Profile</h1>
    <p th:text="#{user.profile.subtitle(${user.name})}">Welcome</p>
  </header>

  <!-- User Information -->
  <div class="profile-content">
    <div class="user-details">
      <span th:text="${user.email}">email@example.com</span>
    </div>
  </div>
</section>

<!-- ❌ Avoid: Unclear structure -->
<div>
  <h1 th:text="#{user.profile.title}"></h1>
  <p th:text="#{user.profile.subtitle(${user.name})}"></p>
  <div>
    <div><span th:text="${user.email}"></span></div>
  </div>
</div>
```

### Consistent Styling

```html
<!-- ✅ Use consistent CSS classes -->
<div class="form-group">
  <label th:for="${field}" class="form-label" th:text="#{form.${field}}"
    >Label</label
  >
  <input th:field="*{${field}}" class="form-control" />
  <div th:if="${#fields.hasErrors(field)}" class="error-message">
    <span th:each="error : ${#fields.errors(field)}" th:text="${error}"></span>
  </div>
</div>

<!-- ✅ Apply consistently -->
<form th:object="${user}">
  <div th:replace="~{fragments/forms :: inputRow('name')}"></div>
  <div th:replace="~{fragments/forms :: inputRow('email')}"></div>
  <div th:replace="~{fragments/forms :: inputRow('phone')}"></div>
</form>
```

### Internationalization

```html
<!-- ✅ Externalize all text -->
<h1 th:text="#{page.title}">Page Title</h1>
<p th:text="#{page.description}">Page Description</p>
<button th:text="#{button.save}">Save</button>

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

<!-- ❌ Avoid hardcoded text -->
<h1>User Profile</h1>
<p>Welcome John!</p>
<button>Save</button>
```

## Form Handling Best Practices

### Form Structure

```html
<!-- ✅ Proper form with validation -->
<form
  th:action="@{/users/save}"
  th:object="${user}"
  method="post"
  class="user-form"
>
  <!-- CSRF protection -->
  <input
    type="hidden"
    th:name="${_csrf.parameterName}"
    th:value="${_csrf.token}"
  />

  <!-- Form fields with labels -->
  <div class="form-group">
    <label for="name" th:text="#{user.name}">Name</label>
    <input
      type="text"
      th:field="*{name}"
      th:classappend="${#fields.hasErrors('name')} ? 'is-invalid' : ''"
      class="form-control"
    />
    <div th:if="${#fields.hasErrors('name')}" class="invalid-feedback">
      <span
        th:each="error : ${#fields.errors('name')}"
        th:text="${error}"
      ></span>
    </div>
  </div>

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

### Validation Display

```html
<!-- ✅ Clear error messages -->
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger">
  <h4 th:text="#{validation.title}">Please correct the errors:</h4>
  <ul>
    <li th:each="error : ${#fields.globalErrors()}" th:text="${error}">
      Error
    </li>
  </ul>
</div>

<!-- ✅ Field-specific errors -->
<div class="form-group">
  <label th:text="#{user.email}">Email</label>
  <input th:field="*{email}" class="form-control" />
  <div th:if="${#fields.hasErrors('email')}" class="error-text">
    <i class="fas fa-exclamation-circle"></i>
    <span
      th:each="error : ${#fields.errors('email')}"
      th:text="${error}"
    ></span>
  </div>
</div>
```

## Accessibility Best Practices

### Semantic HTML

```html
<!-- ✅ Use semantic elements -->
<header>
  <nav th:replace="~{fragments/navigation :: mainNav}">Navigation</nav>
</header>

<main>
  <section aria-labelledby="user-profile-title">
    <h1 id="user-profile-title" th:text="#{user.profile}">User Profile</h1>
    <article class="user-details">
      <!-- User content -->
    </article>
  </section>
</main>

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

### ARIA Attributes

```html
<!-- ✅ Include accessibility attributes -->
<button
  th:attr="aria-label=#{button.delete.user(${user.name})}, 
               aria-describedby=delete-confirm-${user.id}"
  th:id="'delete-btn-' + ${user.id}"
  class="btn btn-danger"
>
  <i class="fas fa-trash"></i>
  <span class="sr-only" th:text="#{action.delete}">Delete</span>
</button>

<div
  th:id="'delete-confirm-' + ${user.id}"
  th:text="#{delete.confirm.message}"
  class="sr-only"
>
  Are you sure you want to delete this user?
</div>
```

### Form Accessibility

```html
<!-- ✅ Accessible form fields -->
<div class="form-group">
  <label th:for="${field}" th:text="#{form.${field}}" class="form-label"
    >Label</label
  >
  <input
    th:field="*{${field}}"
    th:id="${field}"
    th:attr="aria-describedby=${field + '-help'}, 
                   aria-required=${required}"
    th:class="${#fields.hasErrors(field)} ? 'form-control is-invalid' : 'form-control'"
    class="form-control"
  />
  <div
    th:id="${field + '-help'}"
    th:text="#{form.${field}.help}"
    class="form-text text-muted"
  >
    Help text
  </div>
  <div
    th:if="${#fields.hasErrors(field)}"
    th:id="${field + '-error'}"
    class="invalid-feedback"
  >
    <span th:each="error : ${#fields.errors(field)}" th:text="${error}"></span>
  </div>
</div>
```

## Performance Monitoring

### Template Performance Metrics

```java
@Component
public class TemplatePerformanceInterceptor implements HandlerInterceptor {

    @Override
    public void postHandle(HttpServletRequest request,
                       HttpServletResponse response,
                       Object handler,
                       ModelAndView modelAndView) {
        if (modelAndView != null) {
            String viewName = modelAndView.getViewName();
            long startTime = System.currentTimeMillis();

            // Add performance tracking
            modelAndView.addObject("templateStartTime", startTime);
            modelAndView.addObject("viewName", viewName);
        }
    }
}
```

### Performance Optimization Tips

1. **Minimize template complexity** - Avoid deep nesting
2. **Use fragment composition** - Reuse common elements
3. **Optimize iteration** - Use `th:with` for complex expressions
4. **Cache static fragments** - Layout and navigation components
5. **Monitor render times** - Track slow templates
6. **Use appropriate data structures** - Lists vs Sets for different use cases

## Testing Best Practices

### Template Testing Strategy

```java
@SpringBootTest
@AutoConfigureMockMvc
class TemplateTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void shouldRenderUserListCorrectly() {
        ResponseEntity<String> response = restTemplate.getForEntity("/users", String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).contains("John Doe");
        assertThat(response.getBody()).contains("john@example.com");
        assertThat(response.getBody()).contains("<!DOCTYPE html>");
    }

    @Test
    void shouldHandleEmptyUserList() {
        ResponseEntity<String> response = restTemplate.getForEntity("/users", String.class);

        assertThat(response.getBody()).contains("No users found");
        assertThat(response.getBody()).contains("Create User");
    }
}
```

### Integration Testing

```html
<!-- Test-specific attributes -->
<div
  th:attr="data-test-id=user-list, 
               data-test-count=${#lists.size(users)}"
>
  <!-- User list content -->
</div>

<form
  th:attr="data-test-form=user-create, 
             data-test-action=@{/users/save}"
>
  <!-- Form content -->
</form>
```

## Common Anti-Patterns to Avoid

### Performance Issues

```html
<!-- ❌ Avoid: Complex inline expressions -->
<span
  th:text="${#strings.substring(#strings.toUpperCase(user.name), 0, 3) + ' - ' + #strings.substring(user.email, 0, 3)}"
  >Initials</span
>

<!-- ✅ Better: Use utility methods or computed properties -->
<span th:text="${user.initials}">Initials</span>
```

### Security Issues

```html
<!-- ❌ Avoid: Unescaped user input -->
<div th:utext="${user.comment}">Dangerous content</div>

<!-- ❌ Avoid: Hardcoded URLs -->
<a href="/admin/panel">Admin Panel</a>

<!-- ✅ Use: Proper security and URL generation -->
<div th:text="${user.comment}">Safe content</div>
<a th:href="@{/admin/panel}" sec:authorize="hasRole('ADMIN')">Admin Panel</a>
```

### Maintainability Issues

```html
<!-- ❌ Avoid: Repeated code -->
<div class="form-group">
  <label>Name</label>
  <input type="text" name="name" />
</div>
<div class="form-group">
  <label>Email</label>
  <input type="email" name="email" />
</div>

<!-- ✅ Use: Fragments for reusability -->
<div th:replace="~{fragments/forms :: inputRow('name', 'text', true)}"></div>
<div th:replace="~{fragments/forms :: inputRow('email', 'email', true)}"></div>
```

## Development Workflow

### Local Development Setup

1. **Disable template caching** for hot reload
2. **Use browser dev tools** for CSS debugging
3. **Test templates statically** - Open HTML files directly
4. **Validate HTML** - Use HTML validators
5. **Test with real data** - Use realistic test scenarios

### Production Deployment

1. **Enable template caching** for performance
2. **Precompile templates** if possible
3. **Monitor performance** - Track render times
4. **Test thoroughly** - All user paths and error states
5. **Document fragments** - Clear usage guidelines

## Tools and Utilities

### IDE Support

- **Thymeleaf Language Server** - Syntax highlighting and completion
- **Live Reload** - Automatic browser refresh
- **Template Validation** - Real-time error checking
- **Fragment Preview** - Visual fragment testing

### Build Tools

- **Maven/Gradle plugins** - Template validation
- **Spring Boot DevTools** - Automatic restart
- **Docker integration** - Consistent development environment

### Monitoring

- **Application metrics** - Template render times
- **Error tracking** - Template error monitoring
- **Performance profiling** - Identify bottlenecks
