In my previous post on developing my own blog website, I mentioned that I’m using Thymeleaf as the template engine for this project (at least for now). While exploring Thymeleaf's documentation, I discovered two templating approaches that are aptly named: Include-style layouts and Hierarchical-style layouts. The naming is intuitive and reflects how these layouts work. However, while they may look similar at first glance, they serve different use cases.
In this post, I’ll briefly explain the key differences between these styles and provide examples of their practical use.
Example Page Structure
Here’s a basic posts page that we'll use to demonstrate the layout templates:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Posts</title>
<link href="bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="nav">
<ul><li>Home</li><li>Posts</li><li>Projects</li><li>About</li></ul>
</div>
<div class="row">
<div class="col">
<table class="table">
<thead>
<tr>
<th scope="col">Post Title</th>
<th scope="col">Author</th>
<th scope="col">Publish Date</th>
</tr>
</thead>
<tbody>
<tr>
<td>Introduction to Spring Boot</td>
<td>Jane Doe</td>
<td>2024-09-03</td>
</tr>
<tr>
<td>Experiment with AI</td>
<td>John Doe</td>
<td>2024-09-01</td>
</tr>
</tbody>
</table>
</div>
<div class="col">
<div class="tag-cloud">
<ul>
<li>Software Architecture</li>
<li>AI</li>
<li>Spring Boot</li>
<li>Java</li>
</ul>
</div>
</div>
</div>
</div>
<script src="bootstrap.bundle.min.js"></script>
</body>
</html>
Include-Style Layouts
The Include-style layout allows you to insert reusable HTML fragments from other templates into your main template. This approach is ideal for repetitive elements like navigation bars and tag clouds, ensuring that you only need to define them once and include them wherever needed.
For example, we can extract the tag cloud and the navigation bar into separate templates.
tags.html
:
<div th:fragment="tags-cloud" class="tag-cloud">
<ul>
<li>Software Architecture</li>
<li>AI</li>
<li>Spring Boot</li>
<li>Java</li>
</ul>
</div>
navigation.html
:
<div th:fragment="navbar" class="nav">
<ul><li>Home</li><li>Posts</li><li>Projects</li><li>About</li></ul>
</div>
Now we can include these fragments into the posts page using Thymeleaf's th:replace
or th:insert
commands:
posts.html
:
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
<div class="container">
<div th:replace="navigation :: navbar"></div>
<div class="row">
<div class="col">
<table class="table">...</table>
</div>
<div class="col">
<div th:replace="tags :: tags-cloud"></div>
</div>
</div>
</div>
<script src="bootstrap.bundle.min.js"></script>
</body>
</html>
This approach works well when you need to reuse specific elements, such as the navigation bar or tag cloud, across multiple pages. However, as the complexity of your project grows, it may become cumbersome to maintain.
Hierarchical-Style Layouts
The Hierarchical-style layout is a more robust approach when you have multiple templates that share a common structure, such as a header, footer, or layout. It allows you to define a base template that other templates can extend. This makes managing site-wide changes—such as adding an alert bar or custom styling—easier and more maintainable.
Here’s how you can define a base template:
base.html
:
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
<div class="container">
<div th:replace="navigation :: navbar"></div>
<div class="row">
<div class="col">
<div layout:fragment="content"></div>
</div>
<div class="col">
<div th:replace="tags :: tags-cloud"></div>
</div>
</div>
</div>
<script src="bootstrap.bundle.min.js"></script>
</body>
</html>
Then, individual pages can extend this base template:
posts.html
:
<!DOCTYPE html>
<html lang="en" layout:decorate="~{base}">
<head>...</head>
<body>
<div layout:fragment="content">
<table class="table">...</table>
</div>
</body>
</html>
projects.html
:
<!DOCTYPE html>
<html lang="en" layout:decorate="~{base}">
<head>...</head>
<body>
<div layout:fragment="content">
<ul class="projects">...</ul>
</div>
</body>
</html>
With this setup, you can introduce global changes, like adding an alert message or modifying the layout, by updating the base.html
file, without having to modify every individual template.
Conclusion
When I was initially choosing the tech stack for this website, I wasn’t entirely confident in using Thymeleaf for the front-end. However, as I progressed, I realized that it’s an almost perfect fit for my needs at this stage. Thymeleaf offers a range of useful features, on top of Include-style and Hierarchical-style layouts, that make templating both easy and maintainable.
If you are considering Thymeleaf, I highly recommend taking a look at its documentation. It’s a comprehensive and well-structured tool that solves many common templating issues efficiently.