# Ruby on Rails 7: Hotwire va Turbo cho Ung Dung Phan Hoi
> Huong dan day du ve Hotwire va Turbo trong Rails 7. Xay dung ung dung phan hoi khong can JavaScript voi Turbo Drive, Frames va 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 da cach mang hoa viec phat trien web bang cach tich hop Hotwire mac dinh. Stack nay cho phep xay dung cac ung dung co tinh phan hoi cao ma khong can viet bat ky dong JavaScript tuy chinh nao. Turbo Drive, Turbo Frames va Turbo Streams thay the cac cach tiep can SPA truyen thong bang triet ly "HTML qua day".
> **Tai sao Hotwire?**
>
> Hotwire giam manh do phuc tap cua frontend. Khong can React hay Vue cho giao dien dong: server gui HTML san sang su dung va Turbo tu dong xu ly cac cap nhat DOM.
## Hieu ve Kien Truc Hotwire
Hotwire bao gom ba cong nghe bo sung hoat dong cung nhau de mang lai trai nghiem nguoi dung muot ma khong co do phuc tap dien hinh cua cac framework JavaScript.
**Turbo Drive** tang toc dieu huong bang cach chan cac click lien ket va gui bieu mau. Thay vi tai lai toan bo trang, chi noi dung `
` duoc thay the, giu nguyen ngu canh JavaScript va CSS.
**Turbo Frames** chia cac trang thanh cac phan doc lap. Moi frame co the duoc cap nhat rieng biet, cho phep tuong tac co muc tieu ma khong anh huong den phan con lai cua trang.
**Turbo Streams** cho phep cap nhat thoi gian thuc qua WebSocket hoac dap ung cac yeu cau HTTP. Tam hanh dong CRUD co san de thao tac DOM khai bao.
```ruby
# Gemfile
# Installing Turbo Rails (included by default in Rails 7+)
gem 'turbo-rails'
```
Doi voi cac du an hien co, viec cai dat chi can mot lenh duy nhat.
```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"
```
## Cau Hinh Ban Dau Turbo Drive
Turbo Drive duoc bat mac dinh trong Rails 7. Tat ca dieu huong tu dong tro thanh "turbo" ma khong can cau hinh them. Hanh vi co the tuy chinh thong qua cac thuoc tinh data.
```erb
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<%= yield %>
```
Turbo Drive co the duoc tat tren cac lien ket hoac bieu mau cu the khi can thiet.
```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?" } %>
```
> **Cache Turbo Drive**
>
> Turbo Drive luu cache cac trang da truy cap. Thuoc tinh `data-turbo-track="reload"` tren tai nguyen buoc tai lai day du neu cac file CSS/JS thay doi.
## Turbo Frames cho Cap Nhat Co Muc Tieu
Turbo Frames dinh nghia cac vung trang duoc cap nhat doc lap. Moi frame co mot dinh danh duy nhat va chi phan hoi cac response chua frame trung khop.
```erb
Messages
<%= render @messages %>
<%= link_to "Load more", messages_path(page: @next_page) %>
<%= render "form", message: Message.new %>
```
Phan hoi tu server phai chua frame voi cung dinh danh de cap nhat hoat dong.
```erb
```
### Lazy Loading voi Turbo Frames
Cac frame co the tai noi dung bat dong bo su dung thuoc tinh `src`. Noi dung ban dau duoc hien thi trong khi tai.
```erb
Dashboard
Loading statistics...
Loading notifications...
<%= render "activity/placeholder" %>
```
Controller phan hoi voi view chi chua frame duoc yeu cau.
```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 cho Cap Nhat Thoi Gian Thuc
Turbo Streams cung cap tam hanh dong de thao tac DOM: `append`, `prepend`, `replace`, `update`, `remove`, `before`, `after` va `morph`. Cac hanh dong nay co the duoc kich hoat qua phan hoi HTTP hoac 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
```
### Template Turbo Stream Chuyen Dung
Doi voi cac phan hoi phuc tap hon, template `.turbo_stream.erb` mang lai su linh hoat hon.
```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 %>
```
> **Dinh Dang Phan Hoi**
>
> Cac phan hoi Turbo Stream phai co content-type `text/vnd.turbo-stream.html`. Rails xu ly dieu nay tu dong voi `respond_to` va dinh dang `turbo_stream`.
## Broadcasting Thoi Gian Thuc voi Action Cable
Turbo Streams thuc su toa sang voi broadcasting WebSocket. Nguoi dung nhan cap nhat tuc thi ma khong can polling.
```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
```
View dang ky stream tuong ung voi helper `turbo_stream_from`.
```erb
<%= form_with model: [@room, Message.new], id: "new_message" do |f| %>
<%= f.text_area :content, placeholder: "Your message..." %>
<%= f.submit "Send" %>
<% end %>
```
### Broadcasting Bat Dong Bo cho Thao Tac Nang
De tranh chan cac yeu cau, broadcasting co the duoc thuc hien o che do nen.
```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
```
## Cac Mau Nang Cao voi Turbo
### Chinh Sua Bieu Mau Inline
Mot mau pho bien lien quan den viec thay the noi dung tinh bang bieu mau chinh sua inline.
```erb
<% end %>
```
### Dieu Huong Ngoai Frame Cha
Mac dinh, cac lien ket trong frame se o lai trong frame do. Thuoc tinh `data-turbo-frame` cho phep nham den frame khac hoac toan bo trang.
```erb
```
```erb
<%= yield %>
```
> **Debug Turbo**
>
> Trong moi truong phat trien, mo console JavaScript va go `Turbo.setProgressBarDelay(0)` de thay thanh tien trinh ngay lap tuc. `Turbo.session.drive = false` tam thoi tat Turbo Drive.
## Xu Ly Loi va Trang Thai Tai
UX tot doi hoi xu ly cac trang thai tai va loi mang.
```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")
}
}
```
### Xu Ly Loi Server
```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
```
## Toi Uu Hoa Hieu Suat
Nhieu ky thuat dam bao cac ung dung Turbo co hieu suat tot.
```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 %>
```
## Ket Luan
Hotwire va Turbo bien doi viec phat trien Rails bang cach loai bo do phuc tap cua cac framework JavaScript truyen thong. Turbo Drive tang toc dieu huong, Turbo Frames cho phep cap nhat co muc tieu, va Turbo Streams cung cap kha nang thoi gian thuc manh me.
### Danh Sach Kiem Tra de Bat Dau voi Hotwire
- Su dung Rails 7+ de tich hop Hotwire san co
- Hieu ba thanh phan: Drive, Frames va Streams
- Xac dinh cac vung trang se huong loi tu Turbo Frames
- Su dung `respond_to` voi `format.turbo_stream` trong controller
- Trien khai broadcasting cho tinh nang thoi gian thuc
- Ket hop voi Stimulus cho tuong tac JavaScript don gian
Cach tiep can "HTML qua day" nay cho phep xay dung cac ung dung hien dai, phan hoi trong khi duy tri su don gian va nang suat ma Ruby on Rails tro nen manh me. Ket qua: it ma nguon can bao tri hon, hieu suat tuyet voi va trai nghiem nha phat trien toi uu.
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/vi/blog/ruby-on-rails/ruby-on-rails-7-hotwire-turbo