# 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 %>
By <%= message.author.name %> - <%= time_ago_in_words(message.created_at) %>
<%= 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 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
<%= @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
<% @results.each do |result| %>
<%= link_to result.title, result_path(result),
data: { turbo_frame: "_top" } %>
<% end %>
```
```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 %>
Save
<% 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