◀ 3.

テンプレートフラグメント(ヘッダー等の共有化)

共通ヘッダー・フッターはth:fragmentで定義します。呼出元を残すか置き換えるかでinsertとreplaceを選び、旧includeとは出力される要素の違いまで確認します。

replaceは呼出元を置換、insertは部品全体、旧includeは中身だけ挿入する違い

この記事の要点
  • Spring (Thymeleaf) のテンプレートフラグメント: ヘッダー / フッターの共通化
  • 定義: 共通部品ファイルに th:fragment="name" を付ける
  • 読み込み: th:replace="~{path/fragment :: name}" または th:insert
  • 部分置換できるので、ページごとに差分だけ書ける

 

本稿はThymeleafのテンプレートフラグメントをSpringアプリで共有する方法について説明します。Spring Framework本体の機能ではなく、Thymeleafのテンプレート処理です。

 

テンプレートフラグメントとは?

テンプレートフラグメントを利用することで、ヘッダーやフッター等を部品化し、共有化させることができます。

 

フラグメントの作成

この後の元コードは旧th:include方式を読むための例として保持しています。th:includeは非推奨で、3.1では警告対象です。新規の例は後述のth:replace / th:insertと~{...}を使用します。旧例でコードがHTML要素として解釈されていた箇所は、タグ文字列を安全に表示するよう復旧しました。

以下の内容のsrc/main/resources/templates/test/fragment.htmlを作成します。

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">

  <body>

    <!-- Header -->

    <div th:fragment="header">

    <p>ヘッダー</p>

    </div>

 

    <!-- Footer -->

    <div th:fragment="footer">

    <p>フッター</p>

    </div>

  </body>

</html>

 

サンプルページの作成

フラグメントを取り込むサンプルページを作成します。

以下の内容のsrc/main/resources/templates/test/content.htmlを実装します。

<!DOCTYPE HTML>

<html xmlns:th="http://www.thymeleaf.org">

    <head>

        <title>Content Page</title>

        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    </head>

    <body>

    <!-- Header -->

    <div th:include="/test/fragment :: header"></div>

 

    <!-- Content -->

    <p>コンテント</p>

 

    <!-- Footer -->

    <div th:include="/test/fragment :: footer"></div>

    </body>

</html> 

 

出力結果

ヘッダー

コンテント

フッター

 

th:replace・th:insert・旧th:includeの違い

指定呼出元要素フラグメント側の外側要素
th:replace置換される残る
th:insert残る子要素として残る
旧th:include残る取り除かれ、中身だけ入る

includeをinsertへ単純置換すると、元はなかった外側要素が増える場合があります。見た目が同じでも、CSSの子セレクター、JavaScriptのDOM探索、idの重複に影響します。既存レイアウトを移行する場合は、描画後のDOMまで比較してください。フラグメントがheader要素なのにspanの中へ挿入するなど、不適切なHTMLの入れ子も避けます。

Thymeleaf 3.1で検査した2ファイルの例

次の例はThymeleaf 3.1.3.RELEASE / SpringTemplateEngine / Spring 6.2.11 / Java 21で単体描画を検査しています。本番採用する依存版の推奨や、Spring Bootアプリ全体の起動保証ではありません。Bootを使う場合は対応するstarterとBOMに従い、異なるSpring世代のJARを混在させないでください。

src/main/resources/templates/test/fragment.html

header(title)は文字列の引数を受け取ります。th:textで文字として出力し、利用者の入力をth:utextへ渡す例にはしません。footerも同じファイル内に定義します。

<!doctype html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<body>
  <header th:fragment="header(title)">
    <p th:text="${title}">ヘッダー</p>
  </header>
  <footer th:fragment="footer">
    <p>フッター</p>
  </footer>
</body>
</html>

src/main/resources/templates/test/content.html

呼出側のdivをheader/footerへ置換します。id="header-slot"とid="footer-slot"は呼出元divの属性なので、replace後には残りません。必要なidやclassは、実際に残すフラグメント側の要素に設定します。

<!doctype html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Content Page</title></head>
<body>
  <div id="header-slot" th:replace="~{test/fragment :: header('ヘッダー')}"></div>
  <main><p>コンテント</p></main>
  <div id="footer-slot" th:replace="~{test/fragment :: footer}"></div>
</body>
</html>

挿入元のdivも残す場合

次は上記content.html全体の代わりではなく、insertの違いを見る最小断片です。結果はdiv#slotの内側にheaderが残ります。

<div xmlns:th="http://www.thymeleaf.org" id="slot"
     th:insert="~{test/fragment :: header('ヘッダー')}"></div>

テンプレートが見つからないときの確認順

  1. フォルダー名はresourceではなくresourcesです。上記のパスはプロジェクト内の位置で、HTTP URLではありません。
  2. Spring Bootの標準的なclasspath:/templates/と.html接尾辞なら、Controllerが返すビュー名はtest/contentです。独自のTemplateResolverを使う場合は、そのprefix/suffixを確認します。
  3. test/fragmentはテンプレート名、header/footerはフラグメント名です。拡張子やURLを無条件に付け足さず、対象ファイルとth:fragmentの綴り・大文字小文字を確認します。
  4. 新例のheaderはtitle引数が必要です。header('ヘッダー')のように渡します。引数なしの旧header定義と混ぜないでください。
  5. HTMLをファイルとして直接開くだけではth:*は評価されません。テンプレートエンジンを経由した結果を確認します。部品内のCSS/JSが、選択したフラグメントの外側にあれば自動で一緒に取り込まれるとは限りません。

単体試験では、ページ全体の表示順、replace/insert/includeのDOM差、引数の文字出力、テンプレート/フラグメント指定ミスを確認しています。実MVCルーティング・認証・配備先のresolver全体は未検証です。

公式資料

2026年10月確認: フラグメントとレイアウト、旧includeの非推奨、Springとの統合とビュー解決。

子ページ

子ページはありません

同階層のページ
  1. 導入方法と基本動作
  2. メッセージ式
  3. テンプレートフラグメント(ヘッダー等の共有化)

最近更新/作成されたページ