# Ruby on Rails 7: Hotwire y Turbo para Aplicaciones Reactivas > Guia completa de Hotwire y Turbo en Rails 7. Aprende a construir aplicaciones reactivas sin escribir JavaScript con Turbo Drive, Frames y 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 revoluciono el desarrollo web al integrar Hotwire por defecto. Este stack permite construir aplicaciones altamente reactivas sin escribir una sola linea de JavaScript personalizado. Turbo Drive, Turbo Frames y Turbo Streams reemplazan los enfoques SPA tradicionales con una filosofia de "HTML sobre el cable". > **Por que Hotwire?** > > Hotwire reduce drasticamente la complejidad del frontend. No se necesita React ni Vue para interfaces dinamicas: el servidor envia HTML listo para usar, y Turbo se encarga de las actualizaciones del DOM automaticamente. ## Comprendiendo la Arquitectura de Hotwire Hotwire consiste en tres tecnologias complementarias que trabajan juntas para ofrecer una experiencia de usuario fluida sin la complejidad tipica de los frameworks JavaScript. **Turbo Drive** acelera la navegacion interceptando clics en enlaces y envios de formularios. En lugar de recargar toda la pagina, solo se reemplaza el contenido del ``, preservando el contexto de JavaScript y CSS. **Turbo Frames** descomponen las paginas en secciones independientes. Cada frame puede actualizarse por separado, permitiendo interacciones dirigidas sin afectar el resto de la pagina. **Turbo Streams** permiten actualizaciones en tiempo real via WebSocket o en respuesta a peticiones HTTP. Ocho acciones CRUD estan disponibles para la manipulacion declarativa del DOM. ```ruby # Gemfile # Installing Turbo Rails (included by default in Rails 7+) gem 'turbo-rails' ``` Para proyectos existentes, la instalacion requiere solo un 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" ``` ## Configuracion Inicial de Turbo Drive Turbo Drive esta habilitado por defecto en Rails 7. Toda la navegacion se convierte automaticamente en "turbo" sin configuracion adicional. El comportamiento puede personalizarse mediante atributos data. ```erb <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %>
<%= yield %> ``` Turbo Drive puede desactivarse en enlaces o formularios especificos cuando sea necesario. ```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 de Turbo Drive** > > Turbo Drive almacena en cache las paginas visitadas. El atributo `data-turbo-track="reload"` en los assets fuerza una recarga completa si los archivos CSS/JS cambian. ## Turbo Frames para Actualizaciones Dirigidas Turbo Frames definen zonas de la pagina que se actualizan de forma independiente. Cada frame tiene un identificador unico y solo responde a respuestas que contengan un frame coincidente. ```erb

Messages

<%= render @messages %>
<%= link_to "Load more", messages_path(page: @next_page) %>
<%= render "form", message: Message.new %> ``` La respuesta del servidor debe contener un frame con el mismo identificador para que la actualizacion funcione. ```erb

<%= message.content %>

<%= link_to "Edit", edit_message_path(message) %> <%= button_to "Delete", message_path(message), method: :delete %>
``` ### Carga Diferida con Turbo Frames Los frames pueden cargar su contenido de forma asincrona usando el atributo `src`. El contenido inicial se muestra durante la carga. ```erb

Dashboard

Loading statistics...

Loading notifications...

<%= render "activity/placeholder" %> ``` El controlador responde con una vista que contiene unicamente el frame solicitado. ```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 para Actualizaciones en Tiempo Real Turbo Streams ofrece ocho acciones para la manipulacion del DOM: `append`, `prepend`, `replace`, `update`, `remove`, `before`, `after` y `morph`. Estas acciones pueden activarse mediante respuesta 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 ``` ### Plantillas Dedicadas de Turbo Stream Para respuestas mas complejas, una plantilla `.turbo_stream.erb` ofrece mayor flexibilidad. ```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 de Respuesta** > > Las respuestas de Turbo Stream deben tener el content-type `text/vnd.turbo-stream.html`. Rails maneja esto automaticamente con `respond_to` y el formato `turbo_stream`. ## Broadcasting en Tiempo Real con Action Cable Turbo Streams realmente brilla con el broadcasting por WebSocket. Los usuarios reciben actualizaciones al instante sin 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 vista se suscribe al stream correspondiente con el helper `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 %> ``` ### Broadcasting Asincrono para Operaciones Pesadas Para evitar bloquear las peticiones, el broadcasting puede realizarse en segundo plano. ```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 ``` ## Patrones Avanzados con Turbo ### Edicion de Formularios en Linea Un patron comun consiste en reemplazar contenido estatico con un formulario de edicion en linea. ```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 %>
``` ### Navegacion Fuera del Frame Padre Por defecto, los enlaces en un frame permanecen dentro de ese frame. El atributo `data-turbo-frame` permite apuntar a otro frame o a la pagina completa. ```erb ``` ```erb
<%= yield %>
``` > **Depuracion de Turbo** > > En desarrollo, se puede abrir la consola JavaScript y escribir `Turbo.setProgressBarDelay(0)` para ver la barra de progreso inmediatamente. `Turbo.session.drive = false` desactiva Turbo Drive temporalmente. ## Manejo de Errores y Estados de Carga Una buena UX requiere manejar estados de carga y errores de red. ```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") } } ``` ### Manejo de Errores del Servidor ```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 ``` ## Optimizacion del Rendimiento Varias tecnicas aseguran aplicaciones Turbo con buen rendimiento. ```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 %> ``` ## Conclusion Hotwire y Turbo transforman el desarrollo en Rails al eliminar la complejidad de los frameworks JavaScript tradicionales. Turbo Drive acelera la navegacion, Turbo Frames permiten actualizaciones dirigidas, y Turbo Streams ofrece potentes capacidades en tiempo real. ### Lista de Verificacion para Comenzar con Hotwire - Usar Rails 7+ para la integracion integrada de Hotwire - Comprender los tres componentes: Drive, Frames y Streams - Identificar zonas de la pagina que se benefician de Turbo Frames - Usar `respond_to` con `format.turbo_stream` en los controladores - Implementar broadcasting para funcionalidades en tiempo real - Combinar con Stimulus para interacciones JavaScript simples Este enfoque de "HTML sobre el cable" permite construir aplicaciones modernas y reactivas manteniendo la simplicidad y productividad que hacen a Ruby on Rails tan poderoso. El resultado: menos codigo que mantener, excelente rendimiento y una experiencia de desarrollo optima. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/ruby-on-rails/ruby-on-rails-7-hotwire-turbo