Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Общий макет и статические файлы

У всех страниц блога одинаковые шапка с меню, подвал и подключение стилей. Копировать их в каждый шаблон — значит править потом в трёх местах. В Thymeleaf общие куски выносят во фрагменты и вставляют одной строкой. Стили и картинки — статические файлы: Spring Boot отдаёт их без обработки.

Файл с фрагментами

<!-- src/main/resources/templates/fragments/layout.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="head(title)">
    <meta charset="UTF-8">
    <title th:text="${title} + ' | Блог'">Блог</title>
    <link rel="stylesheet" th:href="@{/css/site.css}">
</head>
<body>
<header th:fragment="header">
    <a th:href="@{/}">Блог</a>
    <a th:href="@{/posts/new}">Новая статья</a>
</header>
 
<footer th:fragment="footer">
    <p>Анна и Олег, 2026</p>
</footer>
</body>
</html>

th:fragment даёт куску имя. У фрагмента head есть параметр title — так каждая страница передаст свой заголовок окна.

Вставка фрагментов

<!-- src/main/resources/templates/posts/list.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('Статьи')}"></head>
<body>
<header th:replace="~{fragments/layout :: header}"></header>
 
<h1>Статьи</h1>
<!-- список статей, как в уроке о шаблонах -->
 
<footer th:replace="~{fragments/layout :: footer}"></footer>
</body>
</html>

th:replace="~{fragments/layout :: header}" заменяет тег фрагментом header из файла templates/fragments/layout.html. В скобках после имени передают параметры: на странице статьи это будет head(${post.title}). Так же оформите show.html и form.html.

Стили

/* src/main/resources/static/css/site.css */
body { font-family: sans-serif; max-width: 720px; margin: 0 auto; }
.error { color: #b00020; }

Всё из папки static доступно от корня сайта: файл static/css/site.css откроется по адресу /css/site.css. Ссылку строит th:href="@{/css/site.css}" — она учтёт префикс, если приложение когда-нибудь запустят не в корне сервера.

Результат

curl http://localhost:8080/ теперь возвращает собранную страницу (фрагмент):

<head>
    <meta charset="UTF-8">
    <title>Статьи | Блог</title>
    <link rel="stylesheet" href="/css/site.css">
</head>
<body>
<header>
    <a href="/">Блог</a>
    <a href="/posts/new">Новая статья</a>
</header>

Заголовок окна пришёл из параметра, шапка — из общего файла. Поменяв меню в layout.html, вы поменяете его на всех страницах.

Где что лежит

ПутьАдрес в браузере
src/main/resources/static/css/site.css/css/site.css
src/main/resources/static/img/logo.png/img/logo.png
src/main/resources/templates/posts/list.htmlТолько через контроллер

Шаблоны напрямую по адресу недоступны — их показывает только контроллер.

Частая ошибка

Если ошибиться в пути к фрагменту, например написать ~{fragments/layot :: header}, страница ответит 500, а журнал сообщит Error resolving template [fragments/layot]. Путь в ~{...} считается от папки templates, а после :: идёт имя из th:fragment. И не кладите CSS в templates: файлы оттуда браузер не получит, стили просто не применятся.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.