0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Thymeleaf とalpine.jsを共存させるための実践ノート

0
Last updated at Posted at 2025-12-11

こんにちは。アドベントカレンダーを執筆していきます。

今回はフロントエンドのお話です。Java(Spring Boot)を使った開発を行った際、Thymeleaf(テンプレートエンジン)と Alpine.js(軽量フロントエンドライブラリ)を併用する機会がありました。
この組み合わせはそれほど珍しくないはずなのですが、記法の衝突や、テンプレート生成前後での挙動差などクセがあり、調べてもまとまった情報が少なかったため、備忘録としてまとめます。

本記事の対象者

  • Thymeleaf と Alpine.js を併用しながらフロントエンド開発している方
  • サーバーサイドテンプレート生成後に Alpine.js を適用したい方
  • 記法の相性で混乱している方

なぜ癖があるのか?

結論から言うと、

Thymeleaf と Alpine.js が「コロン : 」を取り合うから

です。

さらに、

  • Thymeleaf は サーバー側で HTML を生成
  • Alpine.js は クライアント側でリアクティブに動作

と、HTML が生成されるタイミングも異なります。

このため、テンプレート内で JS 用の属性を動的に埋め込みたい場合に記法が混在し、特に x-data="" の中で Thymeleaf 変数を使うときに混乱しやすいです。

Thymeleafの基本

Thymeleaf はサーバーサイドで HTML を生成するテンプレートエンジンです。
特徴として:

  • th:* という属性を使って書く
  • 最終的に生成された HTML だけがユーザーへ送られる
  • ${} の EL 式で値を埋め込む

例:

<div th:text="${user.name}"></div>

Alpine.jsの基本

Alpine.js はクライアント側で動作する軽量の挙動制御ライブラリです。

x-data

x-show

x-on:click

動的属性付与 :id="..." :value="..."

など、こちらも コロン : を多用します。

例:

<div x-data="{ open: false }">
  <button x-on:click="open = !open">Toggle</button>
  <p x-show="open">Hello</p>
</div>

これらのth:* の **:(コロン)**が、後述の Alpine.js の記法と衝突します。
また、x-data内にthymeleafからのデータを埋め込みたいときに記法が特殊になります。

本題: Thymeleafとalpine.jsの共存させるには?

ポイントは 2 つです。
x-data をそのまま書くと Thymeleaf がコロンを解釈してしまう
th:x-data を使う
② x-data の中で Thymeleaf の変数を埋め込むときは |…| と ${} を併用

  • Alpine.js のオブジェクト記法:コロン :
  • Thymeleaf の埋め込み:${...}
  • Thymeleaf の文字列囲い:| ... |

実例:li タグのループで値を Alpine 側に渡す場合

Java側でフラグメントを定義します

@RequiredArgsConstructor
@Controller
public class OrderController {

    private final OrderService orderService;

    @GetMapping("/")
    public String getIndex(Model model) {

        List<OrderList> orderList = orderService.getOrderList();

        model.addAttribute("orderList", orderList);
        return "pages/index :: orderList";
    }
}

❌ NG:そのまま書くと壊れる

<li x-data="{ id: ${order.id} }" th:each="order : ${orderList}">

Thymeleaf と Alpine.js の両方が { id: ... } を解釈しようとしてエラーになります。

✅ OK:th:x-data と |...| と ${} を利用

<th:block th:fragment="orderList">
  <li
    th:each="order : ${orderList}"
    th:x-data="|{ id: __${order.id}__ }|"
  >
    <!-- #省略 -->
  </li>
</th:block>

ポイント解説

  • th:x-data="| ... |"
    → Thymeleaf が中身を文字列として扱う
  • id: ${order.id}
    → Thymeleaf の EL 式 ${order.id} を生の値として埋め込む
  • 結果として、クライアントには次のように出力される:
<li x-data="{ id: 123 }">

さいごに

今回は Thymeleaf と Alpine.js を併用する際にハマりやすいポイントと、その記法の整理を紹介しました。
両方とも便利な技術ですが、**「コロン問題」「サーバー生成前後の違い」**に気づかないとかなり混乱します。
同じ技術スタックで開発される方の参考になれば嬉しいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?