# Ruby on Rails 7: Hotwire ta Turbo dlia Reaktyvnykh Dodatkiv > Povnyi posibnyk z Hotwire ta Turbo v Rails 7. Stvorennia reaktyvnykh dodatkiv bez JavaScript z Turbo Drive, Frames ta Streams. - 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 zrobyla revoliutsiiu u veb-rozrobtsi, intehruvavshi Hotwire za zamovchuvannia. Tsei stek dozvoliaie stvoriuvaty vysoce reaktyvni dodatky bez napysannia zhodnoi riadka vlasnoho kodu JavaScript. Turbo Drive, Turbo Frames ta Turbo Streams zaminiuiut tradytsiini pidkhody SPA filosofiieiu "HTML cherez drit". > **Chomu Hotwire?** > > Hotwire drastychno zmenshuie skladnist frontendu. Ne potribno React chy Vue dlia dynamichnykh interfejsiv: server nadsilaie hotovyi do vykorystannia HTML, a Turbo avtomatychno opratsoovuie onovlennia DOM. ## Rozuminnia Arkhitektury Hotwire Hotwire skladaietsia z triokh komplementarnykh tekhnolohii, shcho pratsiuiut razom dlia zabezpechennia plavnoho dosvidu korystuvacha bez typovoi skladnosti JavaScript-freimvorkiv. **Turbo Drive** pryskoruie navigatsiiu, perekhopliuiuchy klyky na posylannia ta vidpravky form. Zamist perenavantazhennia vsiiei storinky, zaminiuietsia lyshe vmist ``, zberigaiuchy kontekst JavaScript ta CSS. **Turbo Frames** rozbyvaiut storinky na nezalezhni sektsii. Kozhen frejm mozhe onovliuvatysia okremo, shcho dozvoliaie tsilespriamovani vzaiemodii bez vplyvu na reshtu storinky. **Turbo Streams** zabezpechuiut onovlennia v realnomu chasi cherez WebSocket abo u vidpovid na HTTP-zapyty. Visim CRUD-dii dostupni dlia deklaratyvnoi manipuliatsii DOM. ```ruby # Gemfile # Installing Turbo Rails (included by default in Rails 7+) gem 'turbo-rails' ``` Dlia isnuiuchykh proektiv instaliatsiia vymahaie lyshe odniiei komandy. ```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" ``` ## Pochatkova Konfiguratsiia Turbo Drive Turbo Drive uvimkneno za zamovchuvannia v Rails 7. Usia navigatsiia avtomatychno staie "turbo" bez dodatkovi konfiguratsiiu. Povedinku mozhna nalashtuvannia cherez data-atrybuty. ```erb <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %>
<%= yield %> ``` Turbo Drive mozhna vymknuty dlia konkretnykh posylan chy form za potreby. ```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?" } %> ``` > **Kesh Turbo Drive** > > Turbo Drive keshuie vidvidani storinky. Atrybut `data-turbo-track="reload"` na resursakh zmushue povne perenavantazhennia, yakshcho faily CSS/JS zminiatsia. ## Turbo Frames dlia Tsilespriamovanykh Onovlen Turbo Frames vyznachaiut zony storinky, shcho onovliuiutsia nezalezhno. Kozhen frejm maie unikalnyi identyfikator i reahuie lyshe na vidpovidi, shcho mistyat vidpovidnyi frejm. ```erb

Messages

<%= render @messages %>
<%= link_to "Load more", messages_path(page: @next_page) %>
<%= render "form", message: Message.new %> ``` Vidpovid servera povianna mistyty frejm z tym samym identyfikatorom, shchob onovlennia pratsiiuvalo. ```erb

<%= message.content %>

<%= link_to "Edit", edit_message_path(message) %> <%= button_to "Delete", message_path(message), method: :delete %>
``` ### Lienyve Zavantazhennia z Turbo Frames Frejmy mozhut zavantazhuvaty svii vmist asynkhronno za dopomohoiu atrybutu `src`. Pochatkovyi vmist vidobrazhaietsia pid chas zavantazhennia. ```erb

Dashboard

Loading statistics...

Loading notifications...

<%= render "activity/placeholder" %> ``` Kontroler vidpovidaie podanniiam, shcho mistyat lyshe zapytuvany frejm. ```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 dlia Onovlen v Realnomu Chasi Turbo Streams proponuie visim dii dlia manipuliatsii DOM: `append`, `prepend`, `replace`, `update`, `remove`, `before`, `after` ta `morph`. Tsi dii mozhut buti zapushcheni cherez vidpovid HTTP abo 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 ``` ### Spetsialni Shablony Turbo Stream Dlia skladnishykh vidpovidei shablon `.turbo_stream.erb` proponuie bilshu hnuchkist. ```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 %> ``` > **Format Vidpovidi** > > Vidpovidi Turbo Stream povynni maty content-type `text/vnd.turbo-stream.html`. Rails opratsoovuie tse avtomatychno z `respond_to` ta formatom `turbo_stream`. ## Broadcasting v Realnomu Chasi z Action Cable Turbo Streams diisnio blyskuche pratsiuie z WebSocket broadcasting. Korystuvachi otrymaiut onovlennia mytt'ievo bez pollinhu. ```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 ``` Podannia pidpysuietsia na vidpovidnyi strim za dopomohoiu helpera `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 %> ``` ### Asynchronnyi Broadcasting dlia Vazhkykh Operatsii Shchob ne blokuvaty zapyty, broadcasting mozhe vykonuvatysia u fonovomu rezhymi. ```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 ``` ## Prosunuti Paterny z Turbo ### Redahuvannia Form v Riadku Poshyrenyi patern poliahaie u zamini statychnoho vmistu formoiu redahuvannia v riadku. ```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 %>
``` ### Navigatsiia Poza Batkivskym Frejmom Za zamovchuvannia, posylannia v frejmi zalyshaiutsia v mezhakh tsoho frejmu. Atrybut `data-turbo-frame` dozvoliaie tsilyty na inshyi frejm abo na vsiu storinku. ```erb ``` ```erb
<%= yield %>
``` > **Vidladka Turbo** > > U seredovyshchi rozrobky vidkryite konsol JavaScript ta vvedite `Turbo.setProgressBarDelay(0)`, shchob odrazhu pobachyty smuzhu prohresu. `Turbo.session.drive = false` tymchasovo vymykaie Turbo Drive. ## Obrobka Pomylok ta Stany Zavantazhennia Dobryi UX vymahaie obrobky staniv zavantazhennia ta merezhnykh pomylok. ```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") } } ``` ### Obrobka Pomylok Servera ```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 ``` ## Optymizatsiia Produktyvnosti Kilka tekhnologij zabezpechuiut produktyvni dodatky 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 %> ``` ## Vysnovok Hotwire ta Turbo transformuiut rozrobku na Rails, usuvaiuchy skladnist tradytsiinykh JavaScript-freimvorkiv. Turbo Drive pryskoruie navigatsiiu, Turbo Frames dozvoliaiut tsilespriamovani onovlennia, a Turbo Streams proponuie potuzhni mozhlyvosti v realnomu chasi. ### Chek-lyst dlia Pochatku Roboty z Hotwire - Vykorystovuvaty Rails 7+ dlia vbudovanoii integratsiiu z Hotwire - Zrozumity try komponenty: Drive, Frames ta Streams - Identyfikuvaty zony storinky, shcho skorystaiiut vid Turbo Frames - Vykorystovuvaty `respond_to` z `format.turbo_stream` v kontrolerakh - Vprovadyty broadcasting dlia funktsii realnoho chasu - Poiednaty zi Stimulus dlia prostykh JavaScript-vzaiemodii Tsei pidkhid "HTML cherez drit" dozvoliaie stvoriuvaty suchasni, reaktyvni dodatky, zberigaiuchy prostotu ta produktyvnist, shcho roblyat Ruby on Rails takym potuzhnym. Rezultat: menshe kodu dlia pidtrymky, chudova produktyvnist ta optymalnyi dosvid rozrobnyka. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/ruby-on-rails/ruby-on-rails-7-hotwire-turbo