# Ruby on Rails 7: Hotwire en Turbo voor Reactieve Applicaties > Volledige gids over Hotwire en Turbo in Rails 7. Bouw reactieve applicaties zonder JavaScript met Turbo Drive, Frames en 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 heeft webontwikkeling gerevolutioneerd door Hotwire standaard te integreren. Deze stack maakt het mogelijk om zeer reactieve applicaties te bouwen zonder een enkele regel aangepast JavaScript te schrijven. Turbo Drive, Turbo Frames en Turbo Streams vervangen traditionele SPA-benaderingen met een "HTML over de draad"-filosofie. > **Waarom Hotwire?** > > Hotwire vermindert de frontend-complexiteit drastisch. Geen React of Vue nodig voor dynamische interfaces: de server stuurt kant-en-klaar HTML en Turbo regelt de DOM-updates automatisch. ## De Hotwire-Architectuur Begrijpen Hotwire bestaat uit drie complementaire technologieen die samenwerken om een vloeiende gebruikerservaring te bieden zonder de typische complexiteit van JavaScript-frameworks. **Turbo Drive** versnelt de navigatie door klikken op links en formulierinzendingen te onderscheppen. In plaats van de hele pagina opnieuw te laden, wordt alleen de inhoud van de `` vervangen, waarbij de JavaScript- en CSS-context behouden blijft. **Turbo Frames** splitsen pagina's op in onafhankelijke secties. Elk frame kan apart worden bijgewerkt, waardoor gerichte interacties mogelijk zijn zonder de rest van de pagina te beinvloeden. **Turbo Streams** maken realtime-updates mogelijk via WebSocket of als reactie op HTTP-verzoeken. Acht CRUD-acties zijn beschikbaar voor declaratieve DOM-manipulatie. ```ruby # Gemfile # Installing Turbo Rails (included by default in Rails 7+) gem 'turbo-rails' ``` Voor bestaande projecten vereist de installatie slechts een commando. ```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" ``` ## Initiele Turbo Drive Configuratie Turbo Drive is standaard ingeschakeld in Rails 7. Alle navigatie wordt automatisch "turbo" zonder extra configuratie. Het gedrag kan worden aangepast via data-attributen. ```erb <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %>
<%= yield %> ``` Turbo Drive kan worden uitgeschakeld voor specifieke links of formulieren wanneer nodig. ```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?" } %> ``` > **Turbo Drive Cache** > > Turbo Drive cachet bezochte pagina's. Het attribuut `data-turbo-track="reload"` op assets forceert een volledige herlaadbeurt als CSS/JS-bestanden wijzigen. ## Turbo Frames voor Gerichte Updates Turbo Frames definieren paginazones die onafhankelijk worden bijgewerkt. Elk frame heeft een uniek identificatienummer en reageert alleen op antwoorden die een overeenkomend frame bevatten. ```erb

Messages

<%= render @messages %>
<%= link_to "Load more", messages_path(page: @next_page) %>
<%= render "form", message: Message.new %> ``` Het serverantwoord moet een frame bevatten met dezelfde identifier om de update te laten werken. ```erb

<%= message.content %>

<%= link_to "Edit", edit_message_path(message) %> <%= button_to "Delete", message_path(message), method: :delete %>
``` ### Lazy Loading met Turbo Frames Frames kunnen hun inhoud asynchroon laden met het `src`-attribuut. De initiele inhoud wordt weergegeven tijdens het laden. ```erb

Dashboard

Loading statistics...

Loading notifications...

<%= render "activity/placeholder" %> ``` De controller antwoordt met een view die alleen het gevraagde frame bevat. ```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 voor Realtime Updates Turbo Streams biedt acht acties voor DOM-manipulatie: `append`, `prepend`, `replace`, `update`, `remove`, `before`, `after` en `morph`. Deze acties kunnen worden geactiveerd via HTTP-antwoord of 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 ``` ### Speciale Turbo Stream Templates Voor complexere antwoorden biedt een `.turbo_stream.erb`-template meer flexibiliteit. ```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 %> ``` > **Antwoordformaat** > > Turbo Stream-antwoorden moeten het content-type `text/vnd.turbo-stream.html` hebben. Rails handelt dit automatisch af met `respond_to` en het `turbo_stream`-formaat. ## Realtime Broadcasting met Action Cable Turbo Streams schittert echt bij WebSocket-broadcasting. Gebruikers ontvangen updates direct zonder 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 ``` De view abonneert zich op de bijbehorende stream met de `turbo_stream_from`-helper. ```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 %> ``` ### Asynchroon Broadcasting voor Zware Operaties Om verzoeken niet te blokkeren, kan broadcasting op de achtergrond worden uitgevoerd. ```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 ``` ## Geavanceerde Patronen met Turbo ### Inline Formulierbewerking Een veelvoorkomend patroon vervangt statische inhoud door een inline bewerkingsformulier. ```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 %>
``` ### Navigatie Buiten het Bovenliggende Frame Standaard blijven links in een frame binnen dat frame. Het `data-turbo-frame`-attribuut maakt het mogelijk om een ander frame of de hele pagina aan te spreken. ```erb ``` ```erb
<%= yield %>
``` > **Turbo Debuggen** > > Open in de ontwikkelomgeving de JavaScript-console en typ `Turbo.setProgressBarDelay(0)` om de voortgangsbalk direct te zien. `Turbo.session.drive = false` schakelt Turbo Drive tijdelijk uit. ## Foutafhandeling en Laadstatussen Een goede UX vereist het afhandelen van laadstatussen en netwerkfouten. ```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") } } ``` ### Serverfoutafhandeling ```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 ``` ## Prestatie-Optimalisatie Meerdere technieken zorgen voor performante Turbo-applicaties. ```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 %> ``` ## Conclusie Hotwire en Turbo transformeren Rails-ontwikkeling door de complexiteit van traditionele JavaScript-frameworks te elimineren. Turbo Drive versnelt de navigatie, Turbo Frames maken gerichte updates mogelijk, en Turbo Streams biedt krachtige realtime-mogelijkheden. ### Checklist om te Beginnen met Hotwire - Rails 7+ gebruiken voor ingebouwde Hotwire-integratie - De drie componenten begrijpen: Drive, Frames en Streams - Paginazones identificeren die baat hebben bij Turbo Frames - `respond_to` met `format.turbo_stream` in controllers gebruiken - Broadcasting implementeren voor realtime-functionaliteit - Combineren met Stimulus voor eenvoudige JavaScript-interacties Deze "HTML over de draad"-benadering maakt het mogelijk om moderne, reactieve applicaties te bouwen met behoud van de eenvoud en productiviteit die Ruby on Rails zo krachtig maken. Het resultaat: minder code om te onderhouden, uitstekende prestaties en een optimale ontwikkelaarservaring. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/ruby-on-rails/ruby-on-rails-7-hotwire-turbo