10分でわかるThymeleaf入門—Spring Bootで動かすサンプル5選
SpringBootで動的HTMLを手軽に書けるテンプレートエンジンとして人気のThymeleaf。しかし「基本構文はわかったけど、実際にどう使えばいいかイメージが湧かない…」という声も多いはず。本記事では、実務3年目エンジニアの佐藤大輔さんが直面しがちな「コードは動くけど味気ない」「たったひと手間でUXを格上げしたい」という悩みを解消すべく、①基本構文のポイント解説+②実践サンプル5選を10分で理解できる構成でお届けします。すぐにコピペできるコード&「なぜこう書くのか」の背景解説で、今すぐプロジェクトに導入可能です!
1. Thymeleafとは?Spring Bootで選ばれる理由
テンプレートエンジンの役割
テンプレートエンジンは、HTMLに動的な値埋め込みや条件分岐、ループ処理を行うライブラリです。JSPやFreemarkerと比べ、Thymeleafはナチュラルテンプレートを採用し、ブラウザで開いてもプレーンHTMLとして動作するため、デザイナーやフロントエンド担当との協業が非常にスムーズになります。
用語解説:ナチュラルテンプレート
ナチュラルテンプレート:HTMLファイル自体に直接Thymeleafの構文を記述する形式。テンプレートエンジンを通さずにブラウザで開いても、プレーンなHTMLとして表示されるのが特徴です。
Thymeleafの特徴と優位点
- 自然なHTML構文:
<span th:text="…">
のように属性だけを追加 - Spring連携が簡単:
spring-boot-starter-thymeleaf
を追加するだけ(MVC構成を理解すれば現場で詰まらない | Spring Boot実装ガイド) - 安全なエスケープ処理:XSS対策済みの変数出力
- フラグメント管理:ヘッダー/フッターなど共通部品を容易に再利用
2. 導入手順:Spring Bootプロジェクトへの組み込み
2.1 依存設定(Maven/Gradle)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
2.2 application.properties の設定
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false # 開発時はfalse推奨
2.3 基本ディレクトリ構成
src/
└ templates/
├ index.html
└ fragments/
├ header.html
└ footer.html
3. 基本構文5選+深掘り解説
ここからはThymeleafの代表的な属性を、「なぜ必要か」「どんな場面で活きるか」「実装時の注意点」とともに具体例でご紹介します。
3.1 th:text
:変数埋め込みの基本
解説
HTML要素のテキストノード部分にサーバーサイドの変数値を安全に埋め込む構文。自動でHTMLエスケープが行われるため、ユーザー入力値をそのまま出力してもXSS攻撃を防げます。
用語解説:XSS(クロスサイトスクリプティング)
XSS:Webサイトの脆弱性を悪用した攻撃手法。悪意のあるスクリプトを埋め込み、訪問ユーザーのブラウザ上で実行させることで、個人情報の窃取などを行うことができます。
例:ユーザー名を表示
// Controller
@GetMapping("/profile")
public String profile(Model model) {
model.addAttribute("username", "<script>alert('XSS')</script>");
return "profile";
}
<p>ようこそ、<span th:text="${username}">[User]</span>さん!</p>
-
動作ポイント
<script>
タグは文字列としてレンダリングされ、実行されません。- 必ず
th:text
を使い、直書きの生HTMLを避けましょう。
3.2 th:each
:リスト/コレクションのループ処理
解説
JavaのList
や配列をHTML要素として反復出力します。ステータス変数を使えばインデックスや初回・最終回判定も可能です。
// Controller
@GetMapping("/products")
public String products(Model model) {
List<Product> products = List.of(
new Product(1, "リンゴ", 120),
new Product(2, "バナナ", 80));
model.addAttribute("products", products);
return "products";
}
<table>
<tr><th>#</th><th>商品名</th><th>価格</th></tr>
<tr th:each="p, stat : ${products}">
<td th:text="${stat.index + 1}">1</td>
<td th:text="${p.name}">商品名</td>
<td th:text="${p.price} + '円'">価格</td>
</tr>
<tr th:if="${products.empty}">
<td colspan="3">商品が見つかりません</td>
</tr>
</table>
動作ポイント
th:each="p, stat : ${products}"
で、stat.index
(0開始)のインデックスが取得できます。- 空リスト時は
products.empty
により「商品が見つかりません」の行が表示されます。 - テーブル表示イメージ:
# | 商品名 | 価格 |
---|---|---|
1 | リンゴ | 120円 |
2 | バナナ | 80円 |
-
注意点:
- コレクションのgetterが正しく命名されていないとデータが渡りません。
- 大量データを一度に出力するとパフォーマンスに影響が出るため、ページネーションや部分更新(Ajax)を検討しましょう。
3.3 th:if
/th:unless
:条件分岐の使い分け
解説
th:if
は条件がtrueのときに要素を出力し、th:unless
はfalseのときに出力。ネストが深くなる場合は<th:block>
でグループ化できます。
例:管理者のみ編集ボタン表示
<div>
<button th:if="${user.admin}" th:onclick="'location.href=\'/edit\''">編集</button>
<p th:unless="${user.admin}">※編集権限がありません</p>
</div>
-
動作ポイント
- Java側で
user
がnullだとNPEになるため、事前チェックを。 - 複数要素をまとめて制御したい場合は
<th:block th:if="…">…</th:block>
を活用。
- Java側で
3.4 @{...}
:パス解決/リンク生成
解説
アプリケーションのコンテキストパスを含めてURLを自動生成。パラメータ付きリンクやクエリ文字列も簡単に記述できます。
例:ユーザー詳細ページへのリンク
<a th:href="@{/users/{id}(id=${user.id})}">詳細へ</a>
-
動作ポイント
{id}
の名前と変数名を一致させる。- クエリ文字列は
@{/search(q=${q},page=1)}
のようにカンマ区切りで。
3.5 th:fragment
:フラグメントによる再利用
解説
部分テンプレート(ヘッダー、フッター、モーダル部品など)を外部ファイルに定義し、必要に応じて挿入・置換できます。
例:共通ヘッダーの定義と挿入
<header th:fragment="mainHeader(title)">
<h1 th:text="${title}">サイトタイトル</h1>
<nav>…</nav>
</header>
<!DOCTYPE html>
<html>
<body>
<!-- 挿入 -->
<div th:insert="fragments/header :: mainHeader('ホーム')"></div>
<p>本文コンテンツ</p>
</body>
</html>
-
動作ポイント
th:insert
は中身だけ挿入、th:replace
はタグごと置換。- 引数付きフラグメントで汎用性を高める。
4. 実践サンプル5選
サンプル①:動的なテーブル一覧表示
(前節3.2の例参照)
サンプル②:フォームのプリセット値&バリデーション連携
<form th:action="@{/user/save}" th:object="${user}" method="post">
<input th:field="*{name}" placeholder="名前" />
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}">エラー</div>
<button type="submit">送信</button>
</form>
用語解説:フォームのプリセット値 / バリデーション
プリセット値:フォームの入力欄にあらかじめ設定しておく値のこと。編集画面などで既存のデータを表示させる際に使います。
バリデーション:入力されたデータが正しい形式や条件を満たしているか検証する仕組み。例えば「メールアドレスの形式が正しいか」などをチェックします。
サンプル③:ネスト構造でのループ表示(DTOリスト)
<div th:each="order : ${orders}">
<h2 th:text="${order.id} + '件目'"></h2>
<ul>
<li th:each="item : ${order.items}" th:text="${item.productName}"></li>
</ul>
</div>
用語解説:DTO(Data Transfer Object)
DTO:データ転送オブジェクトの略。複数のデータをひとまとめにして、コントローラやビュー間でやり取りする際に使用します。無駄なデータを含めず、必要な情報だけを渡すのがポイントです。
サンプル④:カスタムフラグメントを使った共通ヘッダー/フッター
(前節3.5の例参照)
サンプル⑤:Ajax連携で部分更新するビュー
<div id="commentList" th:fragment="list">
<p th:each="c : ${comments}" th:text="${c.text}"></p>
</div>
<script>
fetch('/comments/fragment')
.then(res => res.text())
.then(html => document.getElementById('commentList').innerHTML = html);
</script>
用語解説:Ajax(Asynchronous JavaScript + XML)
Ajax:Webページをリロード(再読み込み)せずにサーバーと通信し、表示内容を動的に更新する技術。非同期通信により、ユーザー体験を向上させることができます。(JavaScriptのAjax完全入門|fetchの使い方・CORSエラーの対処・非同期通信の基本を解説)
5. 実務でありがちな「落とし穴」と対策
- BindingResultを忘れてバリデーションが飛ぶ
public String save(@Valid @ModelAttribute User user, BindingResult result) { … }
→ 順序を@Valid
→BindingResult
に。(Spring Boot バリデーション入門|MVC統合とカスタムアノテーション実践例)
-
ネストDTOで
th:each
が動かない
→ プロパティ名とJavaのgetter名を一致させる。 -
テンプレートキャッシュが開発反映されない
→spring.thymeleaf.cache=false
を設定。
6. ChatGPTでThymeleafコードを自動生成するプロンプト例
「Spring Boot + Thymeleaf で、リスト表示用の<table>と<th:each>によるループ処理、@ControllerからのModel連携まで含めたサンプルコードを教えてください。」
- 生成時の注意:変数名を明確にし、DTOのパッケージimportを補う。(実務で使えるAIプロンプト完全テンプレ|初心者向け構造+成功例付き手順)
7. FAQ(よくある質問)
Q1. ThymeleafとFreemarker、どちらを選べばいい?
自然なHTML編集が優先ならThymeleaf、タグカスタムや高速レンダリングならFreemarker。
Q2. テンプレートキャッシュを無効化するには?
application.properties
にspring.thymeleaf.cache=false
を追加。
Q3. prefers-reduced-motion対応は可能?
CSSで@media (prefers-reduced-motion: reduce)
を使い、アニメーションを抑制。
8. まとめと次に読むべき記事リンク
本記事では、10分で理解できるThymeleafの基本構文と実践サンプル5選を紹介しました。手早くコピペ実装しつつ「なぜこう書くか」まで押さえられる構成なので、ぜひ既存プロジェクトに取り入れてみてください。
参考文献・情報源
- Spring Boot公式スターター: https://spring.io/projects/spring-boot
- Thymeleaf公式ドキュメント: https://www.thymeleaf.org/documentation.html