# Ruby on Rails 7: HotwireとTurboによるリアクティブアプリケーション > Rails 7におけるHotwireとTurboの完全ガイド。Turbo Drive、Frames、Streamsを使用してJavaScriptを書かずにリアクティブアプリケーションを構築する方法。 - Published: 2026-01-11 - Updated: 2026-04-07 - Author: SharpSkill - Tags: ruby on rails, hotwire, turbo, turbo frames, turbo streams - Reading time: 12 min --- Rails 7はHotwireをデフォルトで統合することにより、Web開発に革命をもたらしました。このスタックにより、カスタムJavaScriptを一行も書かずに、高度にリアクティブなアプリケーションを構築できます。Turbo Drive、Turbo Frames、Turbo Streamsは、従来のSPAアプローチを「HTML over the wire」の哲学で置き換えます。 > **なぜHotwireなのか?** > > Hotwireはフロントエンドの複雑さを大幅に削減します。動的なインターフェースにReactやVueは不要です。サーバーがすぐに使えるHTMLを送信し、TurboがDOM更新を自動的に処理します。 ## Hotwireアーキテクチャの理解 Hotwireは3つの補完的なテクノロジーで構成されており、JavaScriptフレームワーク特有の複雑さなしにスムーズなユーザー体験を提供します。 **Turbo Drive**はリンクのクリックとフォーム送信をインターセプトすることでナビゲーションを高速化します。ページ全体を再読み込みする代わりに、``のコンテンツのみが置き換えられ、JavaScriptとCSSのコンテキストが保持されます。 **Turbo Frames**はページを独立したセクションに分解します。各フレームは個別に更新でき、ページの残りの部分に影響を与えずにターゲットを絞ったインタラクションを可能にします。 **Turbo Streams**はWebSocket経由またはHTTPリクエストへのレスポンスとしてリアルタイム更新を実現します。宣言的なDOM操作のための8つのCRUDアクションが利用可能です。 ```ruby # Gemfile # Installing Turbo Rails (included by default in Rails 7+) gem 'turbo-rails' ``` 既存のプロジェクトでは、インストールに必要なコマンドは1つだけです。 ```bash # installation.sh # Installing Turbo in an existing Rails project rails turbo:install # Verify the JavaScript import is present cat app/javascript/application.js # Should contain: import "@hotwired/turbo-rails" ``` ## Turbo Driveの初期設定 Turbo DriveはRails 7でデフォルトで有効になっています。すべてのナビゲーションが追加設定なしで自動的に「turbo」になります。動作はdata属性でカスタマイズできます。 ```erb <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %>
<%= yield %> ``` 必要に応じて、特定のリンクやフォームでTurbo Driveを無効にできます。 ```erb <%= link_to "Dashboard", dashboard_path %> <%= link_to "Download PDF", export_path, data: { turbo: false } %> <%= form_with model: @document, data: { turbo: false } do |f| %> <%= f.file_field :attachment %> <%= f.submit "Upload" %> <% end %> <%= link_to "Delete", item_path(@item), data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %> ``` > **Turbo Driveのキャッシュ** > > Turbo Driveは訪問したページをキャッシュします。アセットの`data-turbo-track="reload"`属性により、CSS/JSファイルが変更された場合に完全な再読み込みが強制されます。 ## ターゲット更新のためのTurbo Frames Turbo Framesは独立して更新されるページゾーンを定義します。各フレームは一意の識別子を持ち、一致するフレームを含むレスポンスにのみ応答します。 ```erb

Messages

<%= render @messages %>
<%= link_to "Load more", messages_path(page: @next_page) %>
<%= render "form", message: Message.new %> ``` 更新が機能するためには、サーバーレスポンスに同じ識別子のフレームが含まれている必要があります。 ```erb

<%= message.content %>

<%= link_to "Edit", edit_message_path(message) %> <%= button_to "Delete", message_path(message), method: :delete %>
``` ### Turbo Framesによる遅延読み込み フレームは`src`属性を使用してコンテンツを非同期的に読み込むことができます。読み込み中は初期コンテンツが表示されます。 ```erb

Dashboard

Loading statistics...

Loading notifications...

<%= render "activity/placeholder" %> ``` コントローラーは要求されたフレームのみを含むビューで応答します。 ```ruby # app/controllers/dashboard_controller.rb class DashboardController < ApplicationController def stats @user_count = User.count @message_count = Message.count @active_today = User.where("last_seen_at > ?", 24.hours.ago).count # Partial rendering for the frame render partial: "dashboard/stats" end end ``` ```erb

<%= @user_count %>

Users

<%= @message_count %>

Messages

<%= @active_today %>

Active today

``` ## リアルタイム更新のためのTurbo Streams Turbo StreamsはDOM操作のための8つのアクションを提供します:`append`、`prepend`、`replace`、`update`、`remove`、`before`、`after`、`morph`。これらのアクションはHTTPレスポンスまたはWebSocket経由でトリガーできます。 ```ruby # app/controllers/messages_controller.rb class MessagesController < ApplicationController def create @message = current_user.messages.build(message_params) respond_to do |format| if @message.save # Turbo Stream response to add the message format.turbo_stream do render turbo_stream: [ turbo_stream.prepend("messages", @message), turbo_stream.update("message_count", partial: "messages/count"), turbo_stream.replace("new_message", partial: "messages/form", locals: { message: Message.new }) ] end format.html { redirect_to messages_path, notice: "Message created" } else format.turbo_stream do render turbo_stream: turbo_stream.replace( "new_message", partial: "messages/form", locals: { message: @message } ) end format.html { render :new, status: :unprocessable_entity } end end end def destroy @message = Message.find(params[:id]) @message.destroy respond_to do |format| # Removal with animation format.turbo_stream { render turbo_stream: turbo_stream.remove(@message) } format.html { redirect_to messages_path, notice: "Message deleted" } end end end ``` ### 専用Turbo Streamテンプレート より複雑なレスポンスの場合、`.turbo_stream.erb`テンプレートがより柔軟性を提供します。 ```erb <%= turbo_stream.prepend "messages" do %> <%= render @message %> <% end %> <%= turbo_stream.update "message_count" do %> <%= Message.count %> messages <% end %> <%= turbo_stream.replace "new_message" do %> <%= render "form", message: Message.new %> <% end %> <%= turbo_stream.prepend "flash_messages" do %>
Message sent successfully!
<% end %> ``` > **レスポンス形式** > > Turbo Streamレスポンスはcontent-type `text/vnd.turbo-stream.html`を持つ必要があります。Railsは`respond_to`と`turbo_stream`フォーマットでこれを自動的に処理します。 ## Action Cableによるリアルタイムブロードキャスティング Turbo StreamsはWebSocketブロードキャスティングで真価を発揮します。ユーザーはポーリングなしで即座に更新を受け取ります。 ```ruby # app/models/message.rb class Message < ApplicationRecord belongs_to :room belongs_to :author, class_name: "User" # Automatic broadcast after creation after_create_commit do broadcast_append_to( room, target: "messages", partial: "messages/message", locals: { message: self } ) end # Broadcast after update after_update_commit do broadcast_replace_to( room, target: dom_id(self), partial: "messages/message", locals: { message: self } ) end # Broadcast after deletion after_destroy_commit do broadcast_remove_to(room, target: dom_id(self)) end end ``` ビューは`turbo_stream_from`ヘルパーで対応するストリームにサブスクライブします。 ```erb

<%= @room.name %>

<%= turbo_stream_from @room %>
<%= render @room.messages.order(created_at: :asc) %>
<%= form_with model: [@room, Message.new], id: "new_message" do |f| %> <%= f.text_area :content, placeholder: "Your message..." %> <%= f.submit "Send" %> <% end %> ``` ### 重い処理のための非同期ブロードキャスティング リクエストをブロックしないために、ブロードキャスティングはバックグラウンドで実行できます。 ```ruby # app/models/report.rb class Report < ApplicationRecord belongs_to :user after_create_commit :generate_async private def generate_async GenerateReportJob.perform_later(self) end end ``` ```ruby # app/jobs/generate_report_job.rb class GenerateReportJob < ApplicationJob queue_as :default def perform(report) # Simulating a long operation report.update!(status: "processing") # Notify user of start report.broadcast_replace_to( report.user, :reports, target: dom_id(report), partial: "reports/report" ) # Generate the report result = ReportGenerator.new(report).generate report.update!(content: result, status: "completed") # Notify user of completion report.broadcast_replace_to( report.user, :reports, target: dom_id(report), partial: "reports/report" ) end end ``` ## Turboの高度なパターン ### インラインフォーム編集 一般的なパターンとして、静的コンテンツをインライン編集フォームに置き換えるものがあります。 ```erb
<%= task.content %>
<%= link_to "Edit", edit_task_path(task) %> <%= button_to "Delete", task_path(task), method: :delete, data: { turbo_confirm: "Delete this task?" } %>
``` ```erb <%= form_with model: @task do |f| %> <%= f.text_field :content, autofocus: true %>
<%= f.submit "Save" %> <%= link_to "Cancel", task_path(@task) %>
<% end %>
``` ### 親フレーム外へのナビゲーション デフォルトでは、フレーム内のリンクはそのフレーム内に留まります。`data-turbo-frame`属性により、別のフレームやページ全体をターゲットにできます。 ```erb ``` ```erb
<%= yield %>
``` > **Turboのデバッグ** > > 開発環境で、JavaScriptコンソールを開いて`Turbo.setProgressBarDelay(0)`と入力すると、プログレスバーがすぐに表示されます。`Turbo.session.drive = false`でTurbo Driveを一時的に無効にできます。 ## エラーハンドリングとローディング状態 良いUXにはローディング状態とネットワークエラーの処理が必要です。 ```erb <%= form_with model: @model, data: { controller: "form-loading", action: "turbo:submit-start->form-loading#disable turbo:submit-end->form-loading#enable" } do |f| %> <%= yield f %> <% end %> ``` ```javascript // app/javascript/controllers/form_loading_controller.js import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["submit", "text", "spinner"] disable() { this.submitTarget.disabled = true this.textTarget.classList.add("hidden") this.spinnerTarget.classList.remove("hidden") } enable() { this.submitTarget.disabled = false this.textTarget.classList.remove("hidden") this.spinnerTarget.classList.add("hidden") } } ``` ### サーバーエラーの処理 ```ruby # app/controllers/application_controller.rb class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound do |exception| respond_to do |format| format.turbo_stream do render turbo_stream: turbo_stream.replace( "main_content", partial: "shared/not_found" ) end format.html { render "errors/not_found", status: :not_found } end end end ``` ## パフォーマンス最適化 いくつかのテクニックにより、パフォーマンスの高いTurboアプリケーションを確保できます。 ```ruby # app/controllers/messages_controller.rb class MessagesController < ApplicationController # Avoid N+1 queries with eager loading def index @messages = Message.includes(:author, :room) .order(created_at: :desc) .page(params[:page]) end # Fragment caching for static elements def show @message = Message.find(params[:id]) fresh_when @message end end ``` ```erb <% cache message do %>
<%= message.content %> By <%= message.author.name %>
<% end %> ``` ## まとめ HotwireとTurboは、従来のJavaScriptフレームワークの複雑さを排除することでRails開発を変革します。Turbo Driveはナビゲーションを高速化し、Turbo Framesはターゲットを絞った更新を可能にし、Turbo Streamsは強力なリアルタイム機能を提供します。 ### Hotwire入門チェックリスト - Hotwireの組み込み統合のためにRails 7以上を使用する - 3つのコンポーネントを理解する:Drive、Frames、Streams - Turbo Framesの恩恵を受けるページゾーンを特定する - コントローラーで`respond_to`と`format.turbo_stream`を使用する - リアルタイム機能のためにブロードキャスティングを実装する - シンプルなJavaScriptインタラクションのためにStimulusと組み合わせる この「HTML over the wire」アプローチにより、Ruby on Railsを強力にするシンプルさと生産性を維持しながら、モダンでリアクティブなアプリケーションを構築できます。結果として、メンテナンスするコードが少なく、優れたパフォーマンス、そして最適な開発者体験が得られます。 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/ruby-on-rails/ruby-on-rails-7-hotwire-turbo