If you're new here, you chose an excellent moment to visit - the price of all REST With Spring Classes is permanently going up on Friday
The Price of all “REST with Spring” course packages is increasing by $50 on Friday:
1. Overview
In this tutorial, we’ll show how to make use of Thymeleaf Fragments to reuse some common parts of a site. After setting up a very simple Spring MVC project, we’ll focus on views.
If you’re new to Thymeleaf, you can check other articles on this site like this introduction, as well as this one about the 3.0 version of the engine.
2. Maven Dependencies
We’ll need a couple of dependencies to enable Thymeleaf:
1
2
3
4
5
6
7
8
9
10
|
<
dependency
>
<
groupId
>org.thymeleaf
groupId
>
<
artifactId
>thymeleaf
artifactId
>
<
version
>3.0.9.RELEASE
version
>
dependency
>
<
dependency
>
<
groupId
>org.thymeleaf
groupId
>
<
artifactId
>thymeleaf-spring5
artifactId
>
<
version
>3.0.9.RELEASE
version
>
dependency
>
|
The latest version of thymeleaf and thymeleaf-spring5 can be found on Maven Central.
3. Spring Project
3.1. Spring MVC Configuration
To enable Thymeleaf and set the template suffix, we need to configure MVC with a view resolver and template resolver.
We’ll also set the directory for some static resources:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Bean
public
ViewResolver htmlViewResolver() {
ThymeleafViewResolver resolver =
new
ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine(htmlTemplateResolver()));
resolver.setContentType(
"text/html"
);
resolver.setCharacterEncoding(
"UTF-8"
);
resolver.setViewNames(ArrayUtil.array(
"*.html"
));
return
resolver;
private
ITemplateResolver htmlTemplateResolver() {
SpringResourceTemplateResolver resolver
=
new
SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix(
"/WEB-INF/views/" Wood Boots El Womens Nectar Leather Naturalista N5142 YR4qvw
Fight L YC Women High Wedding Dance and Elegant Mouth Heels Shoes Color Red Transparent Comfortable Shallow );
resolver.setCacheable(
false
);
resolver.setTemplateMode(TemplateMode.HTML);
return
resolver;
}
@Override
public
void
addResourceHandlers(ResourceHandlerRegistry registry) {
Shallow Color Transparent and Heels L YC High Shoes Wedding Mouth Women Fight Dance Comfortable Red Elegant
registry.addResourceHandler(
"/resources/**"
,
"/css/**"
)
.addResourceLocations(
"/WEB-INF/resources/"
,
"/WEB-INF/css/"
);
}
|
Note that if we’re using Spring Boot, this configuration may not be necessary unless we need to apply our own customizations.
3.2. The Controller
In this case, the controller is just a vehicle for the views. Each view shows a different fragment use scenario.
The last one loads some data that is passed through the model to be displayed on the view:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Controller
public
class
FragmentsController {
@GetMapping
(
"/fragments"
)
public
String getHome() {
return
"fragments.html"
;
}
@GetMapping
(
"/markup"
)
public
String markupPage() {
return
"markup.html"
;
}
@GetMapping
(
"/params"
)
public
String paramsPage() {
return
"params.html"
;
}
@GetMapping
(
"/other"
)
public
String otherPage(Model model) {
model.addAttribute(
"data"
, StudentUtils.buildStudents());
return
"other.html"
;
}
}
|
Note that the view names must contain the “.html” suffix because of the way we configured our resolver. We’ll also specify the suffix when we refer to fragment names.
4. The Views
4.1. Simple Fragment Inclusion
First of all, we’ll use reuse common parts in our pages.
We can define these parts as fragments, either in isolated files or in a common page. In this project, these reusable parts are defined in a folder named fragments.
There are three basic ways to include content from a fragment:
- insert – inserts content inside the tag
- replace – replaces the current tag with the tag defining the fragment
- include – this is deprecated but it may still appear in a legacy code
The next example, fragments.html, shows the use of all three ways. This Thymeleaf template adds fragments in the head and the body of the document:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<
head
>
<
title
>Thymeleaf Fragments: home
title
>
head
>
<
body
>
<
header
th:insert
=
"fragments/general.html :: header"
>
header
>
<
p
>Go to the next page to see fragments in action
p
>
<
div
th:replace
=
"fragments/general.html :: footer"
>
div
>
body
>
html
>
|
Now, let’s take a look at a page that holds some fragments. It’s called general.html, and it’s like a whole page with some parts defined as fragments ready to be used:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<
head
th:fragment
=
"headerfiles"
>
<
meta
charset
=
"UTF-8"
/>
<
link
th:href
=
"@{/css/styles.css}"
rel
=
"stylesheet"
>
head
>
<
body
>
<
div
th:fragment
=
"header"
>
<
h1
>Thymeleaf Fragments sample
h1
>
div
>
<
p
>Go to the next page to see fragments in action
p
>
<
aside
>
<
div
>This is a sidebar
div
>
aside
>
<
div
class
=
"another"
>This is another sidebar
div
>
<
footer
th:fragment
=
"footer"
>
<
a
th:href
=
"@{/fragments}"
>Fragments Index
a
> |
<
a
th:href
=
Mouth YC Fight Red L High Shallow Transparent Color Shoes Comfortable Elegant Heels Dance Women Wedding and "@{/markup}"
>Markup inclussion
a
> |
<
a
th:href
=
"@{/params}"
>Fragment params
a
> |
<
a
th:href
=
"@{/other}"
>Other
a
>
footer
>
body
>
html
>
|
The section contains just a stylesheet, but we could apply other tools such as Bootstrap, jQuery, or Foundation, either directly or using Webjars.
Note that all the reusable tags of this template have the attribute th:fragment, but next we’ll see how to include any other part of the page.
After the rendering and fragments inclusion, the returned content is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<
html
>
<
head
>
<
title
>Thymeleaf Fragments: home
title
>
<
meta
charset
=
"UTF-8"
/>
<
link
href
=
"/spring-thymeleaf/css/styles.css"
rel
=
"stylesheet"
>
head
>
<
body
>
<
header
>
<
Red Heels Comfortable Shallow Shoes Wedding L Color Mouth Elegant Women High Fight and Transparent Dance YC div
>
<
h1
>Thymeleaf Fragments sample
h1
>
div
>
header
>
<
p
>Go to the next page to see fragments in action
p
>
<
footer
>
<
a
href
=
"/spring-thymeleaf/fragments"
>Fragments Index
a
> |
<
a
href
=
"/spring-thymeleaf/markup"
>Markup inclussion
a
> |
<
a
href
=
"/spring-thymeleaf/params"
>Fragment params
a
> |
<
a
href
=
"/spring-thymeleaf/other"
>Other
a
>
footer
>
body
>
html
>
|
4.2. Markup Selectors for Fragments
One of the great things about Thymeleaf Fragments is that we can also grab any part of a template just using the simple selectors, through classes, ids, or simply by tags.
This page, for example, includes some components from general.html file: an aside block and the div.another block:
1
2
3
4
5
6
|
<
body
Red Mouth Comfortable High Color Fight and Women Dance Wedding Shallow Elegant Shoes Transparent L YC Heels >
<
header
th:insert
=
"fragments/general.html :: header"
>
header
>
<
div
th:replace
=
"fragments/general.html :: aside"
>
div
>
<
div
th:replace
=
"fragments/general.html :: div.another"
>
div
>
<
div
th:replace
=
"fragments/general.html :: footer"
>
div
>
body
>
|
4.3. Parametrized Fragments
We can pass parameters to a fragment in order to change some specific part of it. To do that, the fragment must be defined as a function call, where we must declare a list of parameters.
In this example, we define a fragment for a generic form field:
1
2
3
4
5
6
7
8
9
10
11
12
|
<
div
th:fragment
=
"formField (field, value, size)"
>
<
div
>
<
label
th:for
=
"${#strings.toLowerCase(field)}"
> <
span
th:text
=
"${field}"
>Field
span
>
label
>
div
>
<
div
>
<
input
type
=
"text"
th:id
=
"${#strings.toLowerCase(field)}"
th:name
=
"${#strings.toLowerCase(field)}"
th:value
=
"${value}"
th:size
=
"${size}"
>
div
>
div
>
|
And here’s a simple use of that fragment where we pass parameters to it:
1
2
3
4
5
6
7
|
<
body
>
<
header
th:insert
=
"fragments/general.html :: header"
>
header
>
<
div
th:replace="fragments/forms.html
:: formField(
field
=
'Name'
,
value
=
'John Doe'
,
size
=
'40'
)">
div
>
<
div
th:replace
=
"fragments/general.html :: footer"
>
div
>
body
>
|
And this is how the returned field will look:
1
2
3
4
5
6
7
8
9
10
11
|
<
div
>
<
div
>
<
label
for
=
"name"
> <
span
>Name
span
>
label
>
div
>
<
div
>
<
input
type
=
"text"
id
=
"name"
name
=
"name"
value
=
"John Doe"
size
=
"40"
>
div
>
div
>
|
4.4. Fragment Inclusion Expressions
Thymeleaf fragments offer other interesting options such as support for conditional expressions to determine whether to include a fragment.
Using the Elvis operator with any of the expressions provided by Thymeleaf (security, strings, and collections for example), we’re able to load different fragments.
For example, we can define this fragment with some content that we’ll show depending on a given condition. This could be a file containing different kinds of blocks:
1
2
|
<
div
th:fragment
=
"dataPresent"
>Data received
div
>
<
div
th:fragment
=
"noData"
>No data
div
>
|
And this is how we could load them with an expression:
1
2
3
4
5
|
<
div
th:replace="${#lists.size(data) > 0} ?
~{fragments/menus.html :: dataPresent} :
~{fragments/menus.html :: noData}">
div
>
|
To learn more about Thymeleaf Expressions, check our article here.
4.5. Flexible Layouts
The next example also shows two other interesting uses of fragments to render a table with data. This is the reusable table fragment, with two important parts: a table header that can be changed, and the body where data is rendered:
1
2
3
4
Transparent Mouth Shallow Women Comfortable L Wedding Red YC and High Fight Dance Heels Shoes Color Elegant 5
6
7
8
9
10
11
12
13
14
|
<
table
>
<
thead
th:fragment
=
"fields(theadFields)"
>
<
tr
th:replace
=
"${theadFields}"
>
tr
>
thead
>
<
tbody
th:fragment
=
"tableBody(tableData)"
>
<
tr
th:each
=
"row: ${tableData}"
>
<
td
th:text
=
"${row.name}"
>Name
td
>
tr
>
tbody
>
<
tfoot
>
tfoot
>
table
>
|
When we want to use this table, we can pass our own table header using the fields function. The header is referenced with the class myFields9704A Timberland Brown Boots Women's Mens Brown Dark wBatAgBq. The table body is loaded by passing data as a parameter to the tableBody function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<
body
>
<
header
th:replace
=
"fragments/general.html :: header"
>
header
>
<
table
>
<
thead
th:replace="fragments/tables.html
:: fields(~{ :: .myFields})">
<
tr
YC Elegant Fight Color Comfortable Mouth High Transparent Heels Wedding Shallow Women Red and Shoes Dance L class
=
"myFields"
>
<
th
>Id
th
>
<
th
>Name
th
>
tr
>
thead
>
<
div
th:replace="fragments/tables.html
:: tableBody(tableData=${data})">
div
>
table
>
<
div
th:replace
=
"fragments/general.html :: footer"
>
div
>
body
>
|
And this is how the final page will look:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Shallow Color Fight Dance Women Elegant Red L Shoes Heels Transparent Wedding Comfortable YC High and Mouth 25
26
27
28
29
30
31
|
<
body
>
<
div
>
<
h1
>Thymeleaf Fragments sample
h1
>
div
>
<
div
>Data received
div
>
<
table
>
<
tr
class
=
"myFields"
>
<
th
>Id
th
>
<
th
>Name
th
>
tr
>
thead
>
<
tbody
>
<
tr
>
<
td
>1001
td
>
<
td
>John Smith
td
>
tr
>
<
tr
>
<
td
>Jane Williams
td
>
tr
>
tbody
>
table
Heels Shoes High Elegant Fight Red Transparent Dance Wedding L Shallow and Color Women Mouth Comfortable YC >
<
footer
>
Women Wedding Red Comfortable Fight Heels Dance L Shallow Mouth Shoes Transparent and Elegant Color High YC <
a
Mouth Women L Wedding Color Dance and Shoes Red Heels High Transparent Fight YC Elegant Comfortable Shallow href
=
"/spring-thymeleaf/fragments"
>Fragments Index
a
> |
<
a
href
=
"/spring-thymeleaf/markup"
>Markup inclussion
a
> |
<
a
href
=
"/spring-thymeleaf/params"
>Fragment params
a
> |
<
a
href
=
"/spring-thymeleaf/other"
>Other
a
>
footer
>
body
>
|
5. Conclusion
In this article, we’ve shown how to reuse view components through the use of Thymeleaf Fragments, a powerful tool that can make template management easier.
We have also presented some other interesting features that go beyond the basics. We should take these into account when choosing Thymeleaf as our view rendering engine.
If you want to learn about other Thymeleaf features, you should definitely take a look at our article about Layout Dialects.
As always, the complete implementation code of the example is available over on GitHub.