Hotwire en Rails: Turbo Drive, Turbo Frames y Stimulus sin escribir JavaScript

Hotwire es el enfoque de DHH y 37signals para construir aplicaciones web interactivas sin escribir prácticamente nada de JavaScript. La idea central es sencilla: el servidor genera HTML y lo envía al cliente, igual que siempre, pero usando técnicas que hacen que las actualizaciones sean parciales y fluidas sin recargar la página entera. El resultado es una experiencia similar a la de una SPA sin la complejidad de un framework de JavaScript.

Los tres componentes de Hotwire

Hotwire no es una sola librería, son tres piezas que trabajan juntas:

  • Turbo Drive: intercepta los clics en enlaces y envíos de formularios para hacer las peticiones vía fetch en lugar de recargar la página completa.
  • Turbo Frames: permite actualizar partes concretas de la página de forma independiente.
  • Turbo Streams: envía actualizaciones en tiempo real desde el servidor, vía WebSockets o SSE.

Stimulus es el cuarto componente, aunque técnicamente es independiente de Turbo. Añade un poco de JavaScript organizado cuando lo necesitas.

Turbo Drive: navegación sin recargas

Turbo Drive (antes llamado Turbolinks) funciona en cuanto lo cargas. Sin configuración, intercepta los clics en <a> y los envíos de <form>, hace la petición con fetch y sustituye el <body> de la respuesta. El resultado es una navegación que parece más rápida porque el navegador no tiene que repintar el CSS ni re-ejecutar el JavaScript que ya está cargado.

# No hace falta configuración extra en Rails 8
# Turbo Drive está activo por defecto cuando incluyes turbo-rails

# En el controlador, nada cambia
def show
  @usuario = Usuario.find(params[:id])
  # render :show por defecto
end

# Si quieres deshabilitar Turbo Drive en un enlace concreto:
# <a href="/ruta" data-turbo="false">Enlace normal</a>

La mayoría de las aplicaciones Rails funciona con Turbo Drive sin tocar nada. Los problemas aparecen cuando tienes código JavaScript que asume que la página se recarga entre navegaciones (listeners de DOMContentLoaded, por ejemplo). La solución es usar turbo:load en lugar de DOMContentLoaded.

Turbo Frames: actualizaciones parciales

Turbo Frames es donde Hotwire empieza a diferenciarse de Turbolinks. Con un frame, delimitas una parte de la página que puede actualizarse de forma independiente. Cuando el usuario hace clic en un enlace dentro del frame, solo se actualiza esa parte, no toda la página.

# En la vista (app/views/comentarios/index.html.erb)
<%= turbo_frame_tag "lista-comentarios" do %>
  <% @comentarios.each do |comentario| %>
    <div><%= comentario.texto %></div>
  <% end %>
  <%= link_to "Cargar más", comentarios_path(page: @page + 1) %>
<% end %>

# En comentarios_path, la respuesta debe incluir el mismo frame:
# app/views/comentarios/index.html.erb devuelve el mismo turbo_frame_tag
# Rails solo sustituye el contenido del frame, no el resto de la página

Los frames son útiles para paginación inline, modales, formularios en línea y cualquier patrón donde quieres actualizar una sección sin afectar al resto de la página.

Turbo Streams: actualizaciones en tiempo real

Turbo Streams permite enviar actualizaciones desde el servidor que modifican el DOM de forma específica: añadir, reemplazar o eliminar elementos. Funciona tanto en respuestas HTTP normales como vía WebSockets con Action Cable.

# En el controlador
def create
  @comentario = Comentario.new(comentario_params)
  if @comentario.save
    respond_to do |format|
      format.turbo_stream do
        render turbo_stream: turbo_stream.append("lista-comentarios",
          partial: "comentarios/comentario",
          locals: { comentario: @comentario })
      end
      format.html { redirect_to @comentario.post }
    end
  end
end

# En el modelo, para broadcasts automáticos vía WebSocket
class Comentario < ApplicationRecord
  belongs_to :post
  broadcasts_to :post
end

Con broadcasts_to, Rails envía automáticamente un Turbo Stream a todos los usuarios suscritos al canal cuando se crea, actualiza o elimina un comentario. Sin escribir JavaScript.

Stimulus: JavaScript cuando lo necesitas

Stimulus no intenta sustituir a JavaScript, sino organizarlo. Funciona con atributos HTML data-controller, data-action y data-target para conectar elementos del DOM con pequeñas clases JavaScript llamadas controllers.

# app/javascript/controllers/contador_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["contador"]
  static values = { inicio: { type: Number, default: 0 } }

  connect() {
    this.contadorTarget.textContent = this.inicioValue
  }

  incrementar() {
    this.inicioValue++
    this.contadorTarget.textContent = this.inicioValue
  }
}
<div data-controller="contador" data-contador-inicio-value="5">
  <span data-contador-target="contador"></span>
  <button data-action="click->contador#incrementar">+1</button>
</div>

La ventaja de Stimulus es que los controllers se conectan y desconectan automáticamente cuando Turbo añade o elimina elementos del DOM. No tienes que gestionar el ciclo de vida manualmente.

Cuándo Hotwire no es suficiente

Hotwire tiene sus límites. Si necesitas interfaces muy interactivas con estado complejo en el cliente (un editor de documentos colaborativo, un tablero de Kanban con drag and drop sofisticado, una aplicación de gráficos en tiempo real), probablemente necesites React, Vue o similar.

Para el 80% de las aplicaciones web de negocio, Hotwire cubre perfectamente los casos de uso: listados con filtros, formularios con validación en línea, notificaciones en tiempo real, actualizaciones parciales de la página.

Si te interesa el mundo del frontend moderno sin frameworks pesados, en el artículo sobre iterator helpers de JavaScript hay más contexto sobre lo que el lenguaje nativo puede hacer sin librerías.

Empezar con Hotwire en Rails 8

En una aplicación Rails 8 nueva, Hotwire ya viene instalado. Para proyectos existentes:

# Gemfile
gem "turbo-rails"
gem "stimulus-rails"

# Instalar
bundle install
rails turbo:install
rails stimulus:install

La curva de aprendizaje es baja si ya sabes Rails. Los conceptos de Turbo Frames y Streams llevan un par de horas de práctica para asentarse, pero una vez los tienes claros, muchos patrones que antes requerían JavaScript personalizado se resuelven con HTML y unas pocas líneas de Ruby en el controlador.

Imagen: Pexels / César Gaviria

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP