mcpField
T
turbo

turbo

github.com/smnandre開発18077 回インストール19 週間前に更新
リポジトリ

turbo について

Hotwire Turbo でサーバー描画ページに SPA のようなナビゲーションと更新を加える Symfony 開発者向けのリファレンススキルです。Drive、Frames、Streams を使った移動、部分更新、複数要素やリアルタイムの変更を扱います。

主な機能

  1. 01Turbo Drive による AJAX ナビゲーション
  2. 02Frames によるページの部分更新
  3. 03Streams による複数 DOM 要素の更新
  4. 04Frames の遅延読み込み
  5. 05SSE による Mercure ブロードキャスト

活用シーン

  1. 01リンクやフォーム操作での全ページ再読み込みの回避
  2. 02フォームからページの一部分を更新
  3. 03操作後に複数のページ要素を更新
  4. 04接続中のブラウザーへのリアルタイム変更の配信

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/smnandre/symfony-ux-skills --skill turbo

インストール後、Claude Code で/turboを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
api.md
gotchas.md
patterns.md

詳細

Turbo

Hotwire Turbo は、サーバーでレンダリングされた HTML によって、SPA のような速度を実現します。JavaScript を記述する必要はありません。3 つのコンポーネントが連携して動作します。

  • Drive -- すべてのリンクとフォームを自動で AJAX ナビゲーションします(設定不要)
  • Frames -- ページの 1 つのセクションだけを更新する、範囲を限定したナビゲーションです
  • Streams -- サーバーからプッシュされる DOM の変更(追加、置換、削除など)です

判断フロー

Need to update the page?
+-- Full page navigation          -> Turbo Drive (automatic, already active)
+-- Single section from user click -> Turbo Frame
+-- Multiple sections from action  -> Turbo Stream (HTTP response)
+-- Real-time from server/others   -> Turbo Stream (Mercure / SSE)

インストール

composer require symfony/ux-turbo

これで完了です。Turbo Drive はすぐに有効になり、すべてのリンクとフォームが AJAX になります。

Turbo Drive

SPA のような自動ナビゲーションです。すべての <a> のクリックと <form> の送信を捕捉し、AJAX で取得して、<body> を置き換えます。ブラウザーの URL と履歴は通常どおり更新されます。

特定の要素で無効にする

<a href="/external" data-turbo="false">External Link</a>


<div data-turbo="false">
    <a href="/normal">Normal link (no Turbo)</a>
</div>
履歴とキャッシュ

<a href="/page" data-turbo-action="replace">Replace History</a>


<link rel="stylesheet" href="/app.css" data-turbo-track="reload">
<script src="/app.js" data-turbo-track="reload"></script>

Turbo Frames

ページのセクションにナビゲーションの範囲を限定します。フレーム内のリンクとフォームは、そのフレームのコンテンツだけを更新します。ページの残りの部分には影響しません。

基本的なフレーム

<turbo-frame id="messages">
    <h2>Messages</h2>
    <a href="/messages/1">View Message 1</a>  
</turbo-frame>


<turbo-frame id="messages">
    <h2>Message 1</h2>
    <p>Content here...</p>
    <a href="/messages">Back to list</a>
</turbo-frame>

サーバーのレスポンスは完全な HTML ページですが、Turbo は一致する <turbo-frame> だけを抽出して置き換えます。

遅延読み込み

ページのレンダリング後にフレームのコンテンツを非同期で読み込みます。

<turbo-frame id="notifications" src="/notifications" loading="lazy">
    <p>Loading...</p>
</turbo-frame>
別のフレームをターゲットにする

あるフレーム内のリンクから、別のフレームを更新できます。

<turbo-frame id="sidebar">
    <a href="/item/1" data-turbo-frame="main-content">View Item</a>
</turbo-frame>

<turbo-frame id="main-content">
    
</turbo-frame>
フレームから抜ける

フレーム内からページ全体をナビゲートします。

<turbo-frame id="modal">
    <a href="/dashboard" data-turbo-frame="_top">Go to Dashboard</a>
</turbo-frame>
フォーム付きフレーム

フレーム内のフォームは、そのフレーム内で送信され、更新されます。

<turbo-frame id="search-results">
    <form action="/search" method="get">
        <input type="search" name="q">
        <button>Search</button>
    </form>
    <ul>
        {% for item in results %}
            <li>{{ item.name }}</li>
        {% endfor %}
    </ul>
</turbo-frame>
URL の同期

フレームのナビゲーション時にブラウザーの URL を更新します(ブックマーク可能な状態にする場合に便利です)。

<turbo-frame id="products" data-turbo-action="advance">
    
</turbo-frame>

Turbo Streams

1 つのサーバーレスポンスから、複数の DOM 要素を更新します。8 つのアクション(append、prepend、replace、update、remove、before、after、refresh)があり、ID または CSS セレクターで要素を指定します。

Stream アクション
<turbo-stream action="append" target="messages">
    <template><div id="msg_1">New message</div></template>
</turbo-stream>

<turbo-stream action="prepend" target="messages">
    <template><div id="msg_0">First!</div></template>
</turbo-stream>

<turbo-stream action="replace" target="notification">
    <template><div id="notification">Updated!</div></template>
</turbo-stream>

<turbo-stream action="update" target="counter">
    <template>42</template>
</turbo-stream>

<turbo-stream action="remove" target="msg_5"></turbo-stream>

<turbo-stream action="before" target="msg_3">
    <template><div id="msg_2">Inserted before</div></template>
</turbo-stream>

<turbo-stream action="after" target="msg_3">
    <template><div id="msg_4">Inserted after</div></template>
</turbo-stream>

<turbo-stream action="refresh"></turbo-stream>

replace と update では、完全に置き換える代わりにスムーズな DOM モーフィングを行うための、任意の method="morph" 属性を指定できます。

<turbo-stream action="replace" method="morph" target="user-card">
    <template><div id="user-card">Updated content</div></template>
</turbo-stream>
複数の要素をターゲットにする(CSS セレクター)

targets(複数形)と CSS セレクターを使うと、複数の要素に作用させることができます。

<turbo-stream action="remove" targets=".notification.read"></turbo-stream>

<turbo-stream action="update" targets=".price">
    <template>99.00 EUR</template>
</turbo-stream>
Streams 用 Twig コンポーネントの構文

Symfony UX 2.22 以降では、生の HTML の代わりに <twig:Turbo:Stream:*> コンポーネントを使用できます。

<twig:Turbo:Stream:Append target="comments">
    {{ include('comment/_comment.html.twig') }}
</twig:Turbo:Stream:Append>

<twig:Turbo:Stream:Update target="comment-count">
    {{ count }}
</twig:Turbo:Stream:Update>

<twig:Turbo:Stream:Remove target="msg_5" />

Symfony との統合

コントローラーからの Stream レスポンス
use Symfony\UX\Turbo\TurboBundle;

#[Route('/messages', name: 'message_create', methods: ['POST'])]
public function create(Request $request): Response
{
    $message = new Message();
    // ... handle form

    $this->em->persist($message);
    $this->em->flush();

    // Return stream response for Turbo requests
    $request->setRequestFormat(TurboBundle::STREAM_FORMAT);

    return $this->render('message/create.stream.html.twig', [
        'message' => $message,
        'count' => $count,
    ]);
}

プログラムで Stream を構築するには、TurboStreamResponse ヘルパーや TurboStream ヘルパーメソッドも使用できます。

Stream テンプレート
{# templates/message/create.stream.html.twig #}
<turbo-stream action="append" target="messages">
    <template>
        {{ include('message/_message.html.twig', {message: message}) }}
    </template>
</turbo-stream>
<turbo-stream action="update" target="message-count">
    <template>{{ count }}</template>
</turbo-stream>
<turbo-stream action="replace" target="new-message-form">
    <template>
        {{ include('message/_form.html.twig', {message: null}) }}
    </template>
</turbo-stream>
フレームリクエストの検出
public function show(Request $request, int $id): Response
{
    if ($request->headers->has('Turbo-Frame')) {
        $frameId = $request->headers->get('Turbo-Frame');
        // Return only the frame content (or a full page -- Turbo extracts the frame)
    }

    return $this->render('page/show.html.twig');
}
Mercure ブロードキャスト(リアルタイム)

SSE を介して、接続中のすべてのブラウザーに変更をプッシュします。

use Symfony\UX\Turbo\Attribute\Broadcast;

#[Broadcast]
class Message
{
    // Entity changes broadcast automatically to subscribed clients
}
{# Subscribe to Mercure topic #}
<turbo-stream-source src="{{ mercure('chat-room-1')|escape('html_attr') }}">
</turbo-stream-source>

<div id="messages">
    {# Messages appear here in real-time #}
</div>

よくあるパターン

インライン編集

<turbo-frame id="task_{{ task.id }}">
    <span>{{ task.title }}</span>
    <a href="/tasks/{{ task.id }}/edit">Edit</a>
</turbo-frame>


<turbo-frame id="task_1">
    <form action="/tasks/1" method="post">
        <input name="title" value="Task title">
        <button>Save</button>
        <a href="/tasks/1">Cancel</a>
    </form>
</turbo-frame>
フレーム内のモーダル
<turbo-frame id="modal"></turbo-frame>

<a href="/items/1/delete" data-turbo-frame="modal">Delete</a>


<turbo-frame id="modal">
    <dialog open>
        <p>Confirm delete?</p>
        <form method="post">
            <button>Delete</button>
        </form>
        <a href="/items" data-turbo-frame="modal">Cancel</a>
    </dialog>
</turbo-frame>
Stream を使ったフラッシュメッセージ
<turbo-stream action="prepend" target="flash-messages">
    <template>
        <div class="alert alert-success" role="alert">
            Item saved successfully!
        </div>
    </template>
</turbo-stream>

重要な原則

サーバーは完全な HTML ページを返します。 サーバーが常に完全で有効な HTML ページを返す場合に、Turbo は最も効果を発揮します。Turbo Drive は body を置き換え、Turbo Frames は一致するフレームを抽出します。部分的な HTML スニペットを返そうとしないでください(Stream テンプレートを除く)。

フレームの ID は一致している必要があります。 レスポンス内のフレームは、ページ上のフレームと同じ id を持つ必要があります。一致しない場合、Turbo はエラーを表示します。

Streams は副作用に使用します。 1 つのアクションで、ページ内の関連しない複数の部分を更新する必要がある場合に Streams を使用します。1 つのセクションだけを更新する場合は、Frame のほうがシンプルです。

Stimulus は Turbo を補完します。 Turbo はナビゲーションとサーバー通信を処理します。Stimulus はクライアントサイドの動作(アニメーション、切り替え、クリップボード)を処理します。両者は連携して動作します。Stimulus コントローラーは、そのスコープ内での Turbo Frame の置換後も維持され、Drive のナビゲーション時には適切に再接続されます。

参考資料

  • 完全な API(Drive のイベント、Frame の属性、Stream のアクション、Mercure):references/api.md
  • パターン(フォーム、モーダル、検索、ページネーション、無限スクロール):references/patterns.md
  • 注意点(キャッシュの問題、フォームの処理、Stimulus との統合):references/gotchas.md

関連項目

  • UX Map は Turbo Frames 内で動作します。マップの Stimulus コントローラーは、フレームの入れ替え後に正しく再接続されます。
  • UX Icons はインライン SVG であり、特別な処理なしで Turbo Drive のナビゲーションや Frame の入れ替え後も維持されます。

コメント