# Ruby on Rails 7: Hotwire e Turbo per Applicazioni Reattive
> Guida completa a Hotwire e Turbo in Rails 7. Costruire applicazioni reattive senza scrivere JavaScript con Turbo Drive, Frames e 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 ha rivoluzionato lo sviluppo web integrando Hotwire di default. Questo stack consente di costruire applicazioni altamente reattive senza scrivere una singola riga di JavaScript personalizzato. Turbo Drive, Turbo Frames e Turbo Streams sostituiscono gli approcci SPA tradizionali con una filosofia "HTML via cavo".
> **Perche Hotwire?**
>
> Hotwire riduce drasticamente la complessita del frontend. Non servono React o Vue per interfacce dinamiche: il server invia HTML pronto all'uso e Turbo gestisce automaticamente gli aggiornamenti del DOM.
## Comprendere l'Architettura di Hotwire
Hotwire consiste in tre tecnologie complementari che lavorano insieme per offrire un'esperienza utente fluida senza la tipica complessita dei framework JavaScript.
**Turbo Drive** accelera la navigazione intercettando i clic sui link e gli invii dei form. Invece di ricaricare l'intera pagina, viene sostituito solo il contenuto del `
`, preservando il contesto JavaScript e CSS.
**Turbo Frames** scompongono le pagine in sezioni indipendenti. Ogni frame puo essere aggiornato separatamente, consentendo interazioni mirate senza influire sul resto della pagina.
**Turbo Streams** consentono aggiornamenti in tempo reale tramite WebSocket o in risposta a richieste HTTP. Otto azioni CRUD sono disponibili per la manipolazione dichiarativa del DOM.
```ruby
# Gemfile
# Installing Turbo Rails (included by default in Rails 7+)
gem 'turbo-rails'
```
Per i progetti esistenti, l'installazione richiede un solo comando.
```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"
```
## Configurazione Iniziale di Turbo Drive
Turbo Drive e abilitato di default in Rails 7. Tutta la navigazione diventa automaticamente "turbo" senza configurazione aggiuntiva. Il comportamento puo essere personalizzato tramite attributi data.
```erb
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<%= yield %>
```
Turbo Drive puo essere disabilitato su link o form specifici quando necessario.
```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 di Turbo Drive**
>
> Turbo Drive memorizza nella cache le pagine visitate. L'attributo `data-turbo-track="reload"` sugli asset forza un ricaricamento completo se i file CSS/JS cambiano.
## Turbo Frames per Aggiornamenti Mirati
I Turbo Frames definiscono zone della pagina che si aggiornano in modo indipendente. Ogni frame ha un identificatore univoco e risponde solo alle risposte contenenti un frame corrispondente.
```erb
Messages
<%= render @messages %>
<%= link_to "Load more", messages_path(page: @next_page) %>
<%= render "form", message: Message.new %>
```
La risposta del server deve contenere un frame con lo stesso identificatore affinche l'aggiornamento funzioni.
```erb
```
### Caricamento Lazy con Turbo Frames
I frame possono caricare il loro contenuto in modo asincrono usando l'attributo `src`. Il contenuto iniziale viene visualizzato durante il caricamento.
```erb
Dashboard
Loading statistics...
Loading notifications...
<%= render "activity/placeholder" %>
```
Il controller risponde con una view contenente solo il frame richiesto.
```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 per Aggiornamenti in Tempo Reale
Turbo Streams offre otto azioni per la manipolazione del DOM: `append`, `prepend`, `replace`, `update`, `remove`, `before`, `after` e `morph`. Queste azioni possono essere attivate tramite risposta HTTP o 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 Dedicati per Turbo Stream
Per risposte piu complesse, un template `.turbo_stream.erb` offre maggiore flessibilita.
```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 %>
```
> **Formato della Risposta**
>
> Le risposte Turbo Stream devono avere il content-type `text/vnd.turbo-stream.html`. Rails gestisce questo automaticamente con `respond_to` e il formato `turbo_stream`.
## Broadcasting in Tempo Reale con Action Cable
Turbo Streams eccelle davvero con il broadcasting via WebSocket. Gli utenti ricevono aggiornamenti istantaneamente senza 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
```
La view si iscrive allo stream corrispondente con l'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 Asincrono per Operazioni Pesanti
Per evitare di bloccare le richieste, il broadcasting puo essere eseguito in background.
```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
```
## Pattern Avanzati con Turbo
### Modifica Inline dei Form
Un pattern comune consiste nel sostituire il contenuto statico con un form di modifica inline.
```erb
<% end %>
```
### Navigazione Fuori dal Frame Padre
Per impostazione predefinita, i link in un frame rimangono all'interno di quel frame. L'attributo `data-turbo-frame` consente di puntare a un altro frame o all'intera pagina.
```erb
```
```erb
<%= yield %>
```
> **Debug di Turbo**
>
> In sviluppo, aprire la console JavaScript e digitare `Turbo.setProgressBarDelay(0)` per visualizzare immediatamente la barra di avanzamento. `Turbo.session.drive = false` disabilita temporaneamente Turbo Drive.
## Gestione degli Errori e Stati di Caricamento
Una buona UX richiede la gestione degli stati di caricamento e degli errori di rete.
```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")
}
}
```
### Gestione degli Errori del 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
```
## Ottimizzazione delle Prestazioni
Diverse tecniche assicurano applicazioni Turbo performanti.
```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 %>
```
## Conclusione
Hotwire e Turbo trasformano lo sviluppo Rails eliminando la complessita dei framework JavaScript tradizionali. Turbo Drive accelera la navigazione, Turbo Frames consentono aggiornamenti mirati, e Turbo Streams offre potenti capacita in tempo reale.
### Checklist per Iniziare con Hotwire
- Utilizzare Rails 7+ per l'integrazione Hotwire integrata
- Comprendere i tre componenti: Drive, Frames e Streams
- Identificare le zone della pagina che beneficiano dei Turbo Frames
- Utilizzare `respond_to` con `format.turbo_stream` nei controller
- Implementare il broadcasting per le funzionalita in tempo reale
- Combinare con Stimulus per interazioni JavaScript semplici
Questo approccio "HTML via cavo" consente di costruire applicazioni moderne e reattive mantenendo la semplicita e la produttivita che rendono Ruby on Rails cosi potente. Il risultato: meno codice da mantenere, prestazioni eccellenti e un'esperienza di sviluppo ottimale.
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/it/blog/ruby-on-rails/ruby-on-rails-7-hotwire-turbo