HotwireでRailsアプリにインタラクティブ性を素早く追加する方法:初心者向けガイド
現在、HotwireはRails開発者の間で最もホットな話題のひとつです。Railsで開発をしている方なら、すでに耳にする機会が多かったのではないでしょうか。
Hotwireは、ごくわずかなコード量でアプリケーションにインタラクティブ性を追加できるまったく新しいアプローチです。HTMLをそのままネットワーク経由で送信することで驚異的な速度を実現します。つまり、SPA(シングルページアプリケーション)フレームワークに手を染めることなく、レンダリングロジックをサーバーサイドに一元化しながら、高速なページ表示とリッチな操作性を両立できるのです。
この記事では、Hotwireの主要コンポーネントと、Railsアプリでの具体的な活用方法を解説します。まずは「Hotwireとは何か」「なぜ使うべきなのか」から見ていきましょう。
Hotwireとは?
Hotwireは単一のライブラリではなく、HTMLをワイヤー越しに送信することでWeb・モバイルアプリケーションを構築する新しいアプローチの総称です。「Turbo」「Stimulus」、そして「Strada」(当時リリース予定だったコンポーネント)で構成されています。それぞれ詳しくは後述のセクションで説明します。
補足:HotwireはRailsと強く結びついたイメージがありますが、実際には特定の言語に依存しない設計のため、Rails以外のアプリケーションでも問題なく動作します。筆者自身も、Rails以外の複数のアプリや静的サイトでStimulusを本番環境で利用しています。TurboもRailsなしで使うことができます。
それでは、Railsの世界に話を戻しましょう。
RailsアプリでHotwireを使うべき理由
どんな場面でHotwireを使うべきか? 答えはシンプルです。「アプリケーションにインタラクティブ性を加えたいすべての場面」です。たとえば、以下のようなケースが挙げられます。
- ユーザーの操作に応じてコンテンツを条件付きで表示/非表示にしたい場合(例:選択された国に応じて都道府県の一覧が自動的に切り替わる住所フォームなど)
- コンテンツをリアルタイムに更新したい場合(例:Twitterのようなタイムラインで、新しい投稿がページ再読み込みなしに自動追加される仕組み)
- ページの一部を遅延読み込み(Lazy Load)したい場合(例:アコーディオンUIで、タイトルだけ先に表示し、詳細部分は展開時に読み込んで初期表示を高速化する)
Hotwireの主要コンポーネント
前述のとおり、Hotwireは新舊さまざまな技術の集合体です。ここからは、各コンポーネントを順番に見ていきます。
Turbo
Turboの中核を支えているのはHTMLそのものです。Turboは、ネットワークから受け取ったHTMLを処理し、ページ全体を再読み込みすることなくアプリケーションの画面へ反映するためのさまざまな手法を提供します。主な構成要素は以下の4つです。
- Turbo Drive
かつてTurbolinksを使ったことがある方には馴染み深い機能です。JavaScriptがページ内のイベントを横取りし、HTMLを非同期に取得して、既存のマークアップの該当部分を入れ替えます。 - Turbo Frames
ページのマークアップを、独立して読み込み可能なセクション単位に分割できます。
たとえばブログアプリの場合、「記事本文」と「コメント欄」は関連していながら独立した領域です。Turbo Framesで分離しておけば、それぞれのナビゲーションを独立させたり、個別に非同期読み込みしたりできます。 - Turbo Streams
リアルタイムデータをアプリに取り込むためのユーティリティ群です。たとえばTwitter風のニュースフィードを作っているとして、新しいツイートが投稿された瞬間にページを再読み込みせずフィードへ反映したい——そんなとき、Turbo StreamsならJavaScriptを1行も書かずに実現できます。 - Turbo Native
Webアプリケーションの周りにネイティブアプリの外殻(ラッパー)を構築できます。ナビゲーションや操作感がネイティブアプリのように感じられながら、画面をすべてネイティブで作り直す必要はありません。残りの部分は引き続きWebで配信できるため、本当にインタラクティブ性が求められる箇所だけに集中できます。
Stimulus
Stimulusは、HTMLと連携するコントローラーを記述するためのJavaScriptフレームワークです。
ページ上の要素にdata-controller、data-action、data-targetといった属性を付与すると、対応するStimulusコントローラーがその要素にアクセスし、イベントを受け取れるようになります。
この仕組みの優れた点は、関連するStimulusコントローラーのコードを読まなくても、HTMLのdata属性を見るだけで「何をするための要素なのか」が直感的に把握できることです。
これこそがStimulusの核心、すなわち「シンプルさと再利用性」です。一度クリップボードへのコピー用コントローラーを作ってしまえば、別のページでも同じコントローラーを流用でき、マークアップ側にdata-*属性を追加するだけで動作します。
Strada
残念ながら、Stradaについては公開されている情報がまだ多くありません。しかし、HTMLブリッジ属性を介してWebアプリケーションとネイティブアプリが通信し、アクションを実行できるようにするものとされています。
RailsアプリでHotwireを使う方法
インストール手順や基本的な使い方の説明にはあまり時間を割きません。公式のHotwireスクリーンキャストが非常に丁寧なので、詳細はturbo-railsのインストールドキュメントおよびStimulusのインストールガイドを参照してください。
ここからは、実践的なユースケースをいくつか紹介します。
無限スクロールの実装
Turbo Framesを使えば、ユーザーがスクロールするだけで自動的にページネーションされる画面を簡単に作れます。必要なのは次の2点だけです。
- 各「ページ」をそれぞれ専用のフレーム内に描画し、フレームIDにページ番号を含める(例:
turbo_frame_tag "posts_#{@posts.current_page}")。 - 次のページのフレームには
lazyを指定し、画面に入ってくるまで自動読み込みされないようにする。
この例ではKaminariのメソッドを使用していますが、他のページネーション手法にも容易に適応できます。
コントローラー側には特別な実装は不要で、標準的なindexアクションで十分です。
ポイントは「ネストしたフレーム」を使うことです。次のページ用のフレームを、前のページのフレームの内側に配置します。こうすると、最初のページが読み込まれた時点で、次のページ用フレームが末尾に置かれます。ユーザーがそこまでスクロールすると、そのフレームが2ページ目の内容に置き換わり、さらに3ページ目のlazyフレームが末尾に描画されます。これが繰り返されることで無限スクロールが成立します。
動的フォームの実装
Hotwireを使えば、フロントエンドでフィールドの表示切替ロジックを自作することなく、動的フォームを簡単に実装できます。無限スクロールよりはやや複雑で、Turbo StreamとStimulusの両方を組み合わせます。
まずフォームから見てみましょう。ここではNewsとBlogの2つの選択肢を持つkindセレクトボックスを表示しています。選択された種類に応じてカテゴリの選択肢を切り替えたいわけです(categories_for_kind(@post.kind)が指定種類に対応するカテゴリ一覧を返すと仮定します)。
フォームにはdata属性が追加されています。data-targetはフォーム要素をStimulusコントローラー「RefreshFormController」のformターゲットに紐付けます。また、data-action="change->refresh-form#refreshForm"により、kindセレクトが変更されるたびに、紐付いたStimulusコントローラーのrefreshFormメソッドが呼ばれます。
Stimulusコントローラー側の処理はシンプルです。refreshFormが呼ばれるたびに、同じ要素のdata-refresh-form-url属性で指定されたURLへPUTリクエストを送信します。重要なのはresponseKindをturbo-streamに設定している点で、これにより@rails/request.jsライブラリがレスポンスのストリーム内容に基づいた命令を自動的に実行します。
あとは、サーバー側のrefresh_formアクションからTurboが理解できる正しいストリームを返すだけです。投稿の属性を更新した上で、レスポンス形式をturbo_streamに指定します。そうすることでrefresh_form.turbo_stream.erbテンプレートが参照されます。
このテンプレートでは、既存のformパーシャルを再利用し、replaceアクションを持つturbo_streamで包んで返しています。
以上で動的フォームの完成です。少し高度に見えるかもしれませんが、このrefresh用Stimulusコントローラーは共通部品なので、適切なdata-*属性を追加するだけで他のどの動的フォームにも使い回せます。つまり、以降は新しいJavaScriptを一切書かずに、サーバーサイド主導の動的フォーム更新が実現できるのです。かなり強力だと思いませんか?
ページ再読み込みなしでコンテンツを追記する
Hotwireが得意とするもうひとつのユースケースが、WebSocket接続経由でHTMLをストリーミングし、届いたコンテンツでページを随時更新する仕組みです。良い例がGitHubのコメント欄でしょう。これもTurbo Streamsを使えば非常に簡単に実装できます。
実装は2段階です。
まず、一覧表示ページにTurbo Streamのリスナーを埋め込みます。これはサーバーへのWebSocket接続を開き、イベントを待ち受ける役割を担います。
次に、モデルを更新して、新しいコメントが作成されるたびにストリームへブロードキャストするようにします。
これだけで完了です。新しいコメントごとに、Turboが自動的にapp/views/comments/_comment.html.erbパーシャルを描画し、WebSocket経由で送信します。クライアント側のTurbo JSがそれを受信し、idがcommentsのターゲット要素の先頭に追加します。
さらに一歩進めて、新着コメントにハイライト表示を加えてみましょう。小さなStimulusコントローラーを用意します。
まず、ブロードキャスト設定とcommentパーシャルを修正し、このコントローラーを条件付きで読み込むようにします。このコントローラーは接続時に特殊なハイライトクラスを3秒間だけ付与し、その後削除するというシンプルなものです。
注意:そのクラスの有無に応じたCSSのハイライトスタイルも併せて定義しておく必要があります。
このコントローラーが完成すれば、ハイライトクラスが必要になるあらゆる場面で再利用できます。柔軟性が必要なら、表示時間やクラス名をdata属性から受け取れるよう拡張してもよいでしょう。
ここにこそHotwireの魅力があります。ほとんどの場面でJavaScriptに手を触れずに済み、どうしてもJSが必要になったときも、Stimulusを使えば汎用的で再利用可能な小さなコントローラーを構築できるのです。
まとめとさらに学ぶために
Hotwireの登場にRailsコミュニティが大きな熱狂を見せているのは、決して偶然ではありません。
この記事では、Hotwireの主要コンポーネントとRailsアプリでの活用方法を解説し、TurboとStimulusでアプリケーションに命を吹き込む方法に触れました。
まずは公式のHotwireスクリーンキャスト入門編とTurboドキュメントを読むのが、HotwireとTurboで何ができるのかを把握する近道です。
より高度な使い方を知りたい方は、turbo-railsのGitHubリポジトリを覗いてみましょう。残念ながらドキュメントはまだ乏しいのですが、コードを読むことに抵抗がなければ、以下のソースとインラインコメントが参考になります。
Turbo::FramesHelper—— Turbo FramesについてTurbo::Broadcastable—— コードからTurbo Streamsへブロードキャストする方法Turbo::Streams::TagBuilder—— コントローラーアクション内でTurbo Streamsへブロードキャストする方法
それでは、Happy coding!
P.S. Ruby Magicの最新記事をいち早くお読みになりたい方は、Ruby Magicニュースレターを購読して、記事を見逃さないようにしましょう!

著者プロフィール:Sapan Diwakar
ゲスト執筆者のSapan Diwakarはフルスタック開発者です。自身のブログで関心のあるテーマについて執筆しており、「人生においてもコードにおいても、物事はシンプルに保つべきだ」という信念の持ち主です。技術の仕事をしていないときは、ガーデニング、森の中のハイキング、屋外スポーツを楽しんでいます。
-
Rails 7がまもなく登場!主要な新機能とアップグレードのポイントを徹底解説
Rails 7の公開が目前に迫っています。正式なリリース日は確定していませんが、クリスマス前には利用可能になると予想されており、もうすぐそこまで来ています。 本記事の執筆時点での最新バージョンは7.0.0.rc1、つまり最初のリリース候補版(RC)です。Basecamp、HEY、GitHub、Shopifyといった大手企業がすでに本番環境でRails 7のアルファ版を稼働させているため、リリース候補版もかなり安定していることが期待できます。 この記事では、Rails 7がもたらす新機能と変更点について詳しく見ていきます。 Node.jsとWebpackが不要に そう、お読みの通りです!Rai
-
Rubyでアプリケーションサーバーが必要な理由とは?Pumaの役割をわかりやすく解説
rails server を実行したときに、自動的に起動してくる「Puma」という存在を見たことはありませんか? 実はあれは「アプリケーションサーバー」です。この記事では、アプリケーションサーバーとは何か、なぜ必要なのかを具体例とともにわかりやすく解説します。 アプリケーションサーバーとは何か まず、Rubyで新しいWebアプリケーションの開発を始めた場面を想像してみてください。 コードを一行も書く前に、ブラウザでページが表示されることを確認したいですよね。デフォルトの「Welcome」ページでもいいので、とりあえず表示させたいはずです。 そこでブラウザを開き、localhost:3000(S