LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1818 min read

Templating Engine Dependencies

Cover spring-boot-starter-thymeleaf for server-rendered HTML, with brief comparisons against the FreeMarker and Mustache starters.

Introduction

Not every Spring Boot application is a pure JSON API behind a separate frontend — plenty still render HTML directly on the server, whether for an admin dashboard, an email template, or a whole traditional web app. This lesson covers the three templating engine starters Spring Boot supports out of the box, with Thymeleaf as the primary focus since it is the most commonly used with Spring.

What You Will Learn
  • How spring-boot-starter-thymeleaf renders server-side HTML views from a controller.
  • What FreeMarker offers as an alternative templating engine.
  • What Mustache's "logic-less" template philosophy means in practice.
  • How to choose between the three for a given project.

Thymeleaf: Server-Rendered HTML

Thymeleaf templates are valid HTML on their own — they can be opened directly in a browser and look reasonable even without a running server, which makes them easy for designers to work with. Spring populates the dynamic parts through special th:* attributes at render time.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
@Controller
public class ProductPageController {
private final ProductService productService;
public ProductPageController(ProductService productService) {
this.productService = productService;
}
@GetMapping("/products/{id}")
public String showProduct(@PathVariable String id, Model model) {
Product product = productService.findById(id);
model.addAttribute("product", product);
return "product-detail";
}
}
src/main/resources/templates/product-detail.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="${product.name}">Product</title>
</head>
<body>
<h1 th:text="${product.name}">Product Name</h1>
<p th:text="'Price: $' + ${product.price}">Price</p>
<ul>
<li th:each="tag : ${product.tags}" th:text="${tag}">tag</li>
</ul>
</body>
</html>
Rendered Output for /products/p1

Click Run to see what this code prints.

The return value "product-detail" from the controller maps to templates/product-detail.html by convention, and every th:* attribute is stripped and replaced with real content by the time the response reaches the browser.

FreeMarker: An Alternative

FreeMarker uses its own template syntax (${...} for expressions, <#if>/<#list> directives) rather than embedding logic in HTML attributes. It predates Thymeleaf and is still common in projects that migrated from older Java web frameworks, or that need to generate non-HTML output like emails or config files from templates.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
src/main/resources/templates/product-detail.ftlh
<h1>${product.name}</h1>
<p>Price: $${product.price}</p>
<ul>
<#list product.tags as tag>
<li>${tag}</li>
</#list>
</ul>

The controller code is identical to the Thymeleaf example above — only the template file extension (.ftlh instead of .html) and its internal syntax change, since Spring MVC's view resolution abstracts the two engines behind the same Model/return-string pattern.

Mustache: Logic-Less Templates

Mustache deliberately supports no real logic in the template itself — no if/else, no arithmetic, only variable substitution and simple list iteration via {{#section}} blocks. Everything more complex has to be computed in Java before it reaches the template, which keeps templates simple but pushes more work onto the controller or service layer.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mustache</artifactId>
</dependency>
src/main/resources/templates/product-detail.mustache
<h1>{{product.name}}</h1>
<p>Price: ${{product.price}}</p>
<ul>
{{#product.tags}}
<li>{{.}}</li>
{{/product.tags}}
</ul>

Comparison Table

EngineTemplate SyntaxValid HTML on Its OwnLogic in Templates
ThymeleafHTML attributes (th:*)YesModerate — iteration, conditionals
FreeMarkerOwn syntax (#if, #list)NoExtensive — full directive language
MustacheDouble-brace tags ({{ }})NoMinimal — variables and simple loops only

Thymeleaf is the most common default for Spring MVC projects specifically because it stays valid, previewable HTML. FreeMarker suits teams that want a more powerful template language or need to generate non-HTML text. Mustache suits teams that deliberately want to keep all real logic out of templates and in application code.

Common Mistakes

Avoid These Mistakes
  • Adding more than one templating starter at once without realizing it — Spring Boot will pick one resolution order, and having two active engines usually indicates a leftover dependency rather than an intentional choice.
  • Forgetting that Thymeleaf caches templates in production by default, so a change to an HTML file will not appear without a restart unless devtools (covered in the previous lesson) is active.
  • Putting business logic directly into a FreeMarker template because the syntax allows it, making the logic hard to unit test.
  • Returning a template name from a @RestController instead of a @Controller — @RestController serializes the return value as the response body directly rather than resolving it as a view.

Frequently Asked Questions

Yes — use @Controller with a Model and a view name for the HTML pages, and @RestController for the JSON endpoints. They coexist without conflict in the same Spring Boot app.

Technically yes with extra configuration, but it is discouraged for Spring Boot specifically — JSP does not work well with the embedded servlet containers Spring Boot uses by default, and Thymeleaf is the recommended replacement.

For typical web page rendering the difference is rarely significant enough to be the deciding factor — template complexity and caching configuration usually matter more than the engine choice itself.

Summary

Thymeleaf, FreeMarker, and Mustache all plug into the same Spring MVC view-resolution mechanism but differ in syntax philosophy — from Thymeleaf's HTML-first attributes to Mustache's deliberately logic-less tags. Thymeleaf remains the most common default for new Spring Boot projects. Next, we move on to sending email from a Spring Boot application.

Next Lesson →

Email Dependencies