27

I'm a Thymeleaf beginner. I started with a common layout page:

fragments/layout.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerFragment">
    <title>Template title</title>
    <!-- metas, link and scripts -->
</head>
<body>
<div class="container">
    Some text
</div>
</body>
</html>

And a content page:

page.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:include="fragments/layout :: headerFragment">
    <title>Page title</title>
    <!-- metas, link and scripts -->
</head>
<body>
<div class="container">
    Some text
</div>
</body>
</html>

When I render the page, the title is always from the template, not from the page. Is it possible in Thymeleaf to have metas, scripts and style in common (in the HEAD tag) but to have a per-page title?

Mahozad
  • 18,032
  • 13
  • 118
  • 133
Andrea Colleoni
  • 5,919
  • 3
  • 30
  • 49

8 Answers8

27

I was also having this problem (Thank you nmy for referencing the documentation!) Here is what I noticed and how I solved it in my app:

Things to note from the documentation:

  1. The differences between th:include and th:replace
  2. Referencing fragments by domselector instead of by th:fragment
  3. Thymeleaf provides a "this" option for finding selectors

With these 3 things in mind, you can do the following:

fragments/layout.html:

<head th:fragment="headerFragment">
    <title th:include=":: #pageTitle">Layout Generic Title< /title>
    <!-- metas, link and scripts -->
</head>

page.html

<head th:include="fragments/layout :: headerFragment">
    <title id="pageTitle">Page title</title>
    <!-- other elements you want to reference in your layout -->
</head>

Hope this helps!

Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
tarish
  • 371
  • 2
  • 4
20

Check out Parameterizable fragment signatures.

Basically, in your fragment:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerFragment (pageTitle)">
    <title th:text="${pageTitle}>Template title</title>
    <!-- metas, link and scripts -->
</head>
<body>
    <div class="container">
        Some text
    </div>
</body>
</html>

Then, where you use it:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/layout :: headerFragment (pageTitle='Bla Bla Some Title'">
    <title>Page title</title>
    <!-- metas, link and scripts -->
</head>
<body>
    <div class="container">
        Some text
    </div>
</body>
</html>
demaniak
  • 3,716
  • 1
  • 29
  • 34
19

This is the best solution i found:

  1. Create a block fragment

<th:block th:fragment="header">

    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" charset="UTF-8"/>
    <meta name="_csrf" th:content="${_csrf.token}"/>
    <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>

    <!-- Bootstrap Css -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"/>

    <!-- Fontawesome css -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/>

    <!-- JQuery -->
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

</th:block>
  1. Do this in all the heads for your html pages

<head>
    <div th:replace="fragments/headerFragment :: header"></div>
    <title>Example</title>
    <script src="customStuff"></script>
</head>

So in the end you can have one fragment containing all the scripts and css you need in all your pages and just add it to your headers, while still being able to ad some custom css, scripts or what ever

Yocker95k
  • 231
  • 2
  • 9
  • 3
    You may also add `th:remove="tag"` to the div and you'll get a clean head without unneeded 'div' tag (see https://stackoverflow.com/a/35991030). – Siggen Dec 29 '19 at 16:18
9

You can even combine them ;) Use the following in the template page.

<title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">
    Template title
</title>

In your case, this would resolve in:

<title>Template title - Page Title</title>
MystyxMac
  • 1,532
  • 2
  • 14
  • 28
  • 1
    Your answer isn't complete.You should also mention that the layout attribute requires the Thymeleaf Layout Dialect dependency. It won't work with just basic Thymeleaf – aardbol Jul 26 '18 at 01:35
3

This work for me..

layout

<head th:fragment="headerfragment">
    <title th:text="${pageTitle}">Template Title</title>

page

<head th:include="layout :: headerfragment">

on my Controller

m.addAttribute("pageTitle", "Dashboard Page");
TopekoX
  • 319
  • 2
  • 5
0

According to the documentation th:include includes the content of a fragment into the including div. So you will get the title of template. You can use an attribute for the page title as follows and set its value in each controller.

<head th:fragment="headerFragment">
   <title>${pagetitle}</title>
   <!-- metas, link and scripts -->
</head>

Alternatively you can use layout dialects to achieve the same as described in here. Thymeleaf layout dialect and th:replace in head causes title to be blank

Community
  • 1
  • 1
nmy
  • 488
  • 8
  • 21
0
    <!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">
  <head th:fragment="static_resource">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all"
        href="../lib/element-ui/index.css"  />
    <script type="text/javascript" src="../lib/http.js"></script>
    <script type="text/javascript" src="../lib/vue.js"></script>
    <script type="text/javascript" src="../lib/element-ui/index.js"></script>
  </head>
  <body>
    
  
  </body>
  
</html>

and include the fragment with block directive,can set title separation

<head>
<th:block th:include="web/fragments/static_file :: static_resource" />
<title>首页</title>
</head>
Mr Lou
  • 1,757
  • 19
  • 19
  • This example is working because the template(fragment) doesn't contain a `title` element. If you include a title in both the template and the page then the title will be rendered from template (not from page). – Georgios Syngouroglou Jan 28 '21 at 11:32
0

1) Compose fragments

What you want to do is let a HTML inherit from another HTML, and override a part of code. But I don't recommend to use Inheritance (th:include), you can see a lot of discussion everywhere about the difference between Inheritance and composition.

Here is the fragment file(templates/fragments/header.html):

<div th:fragment="headerMeta" xmlns:th="http://www.thymeleaf.org" th:remove="tag">
    <!-- meta -->
    <meta charset="UTF-8">
</div>

<div th:fragment="headerCssJs" xmlns:th="http://www.thymeleaf.org" th:remove="tag">
    <!-- CSS and JS -->
    <script src="https://code.jquery.com/jquery-3.5.1.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
</div>

Here is the template file:

<head>
    <title>My First Page</title>
    <meta name="description" content="This is my first page.">
    
    <div th:replace="fragments/header :: headerMeta"></div>
    <div th:replace="fragments/header :: headerCssJs"></div>
</head>

2) Example in Official site

8.3 Flexible layouts: beyond mere fragment insertion

Jess Chen
  • 3,136
  • 1
  • 26
  • 35