# Rails Turbo en Hotwire in 2026: Real-Time Applicaties en Interviewvragen > Rails Turbo en Hotwire leveren real-time interactiviteit aan webapplicaties zonder JavaScript-frameworks. Turbo 8 morphing, broadcasts_refreshes, Stimulus-integratie. - Published: 2026-07-19 - Updated: 2026-07-19 - Author: SharpSkill - Reading time: 5 min --- Rails Turbo en Hotwire leveren real-time interactiviteit aan webapplicaties zonder aangepaste JavaScript-frameworks. Het ecosysteem is in 2026 aanzienlijk gerijpt, met Turbo 8's morphing-mogelijkheden en Rails 8's verbeterde broadcasting die reactieve UI's haalbaar maken met minimale code. Dit artikel behandelt productiepatronen, interviewvoorbereiding en implementatiedetails voor het bouwen van moderne Rails-applicaties. > **Belangrijke Inzicht** > > Turbo 8's morphing met `broadcasts_refreshes` maakt real-time updates mogelijk met een enkele regel Ruby-code. De server broadcast een signaal, clients morphen de DOM en de scrollpositie wordt automatisch bewaard. ## De Hotwire-Architectuur Begrijpen Hotwire bestaat uit drie complementaire technologieën die samenwerken om reactieve webervaringen te creëren. Turbo Drive onderschept navigatie en formuliersubmissies en vervangt pagina-inhoud via AJAX zonder volledige herlaadacties. Turbo Frames beperken updates tot specifieke paginaregio's, waardoor inline bewerking en lazy loading mogelijk worden. [Turbo Streams](https://turbo.hotwired.dev/) leveren granulaire DOM-wijzigingen via WebSocket-verbindingen of HTTP-responses. De architectuur volgt een server-centrisch model waarbij HTML de primaire payload blijft. In tegenstelling tot SPA's die JSON overdragen en op de client renderen, renderen Hotwire-applicaties HTML server-side en streamen het naar browsers. Deze aanpak vereenvoudigt state management, verbetert SEO en vermindert JavaScript-bundelgroottes. Rails 8 wordt standaard geleverd met Hotwire 2.0 en integreert [Action Cable](/technologies/ruby-on-rails/interview-questions/action-cable-websockets) voor WebSocket-ondersteuning. Het framework handelt verbindingsbeheer, channel-subscripties en broadcasting af zonder handmatige configuratie. ## Turbo Drive: Zero-Configuratie SPA-Navigatie Turbo Drive transformeert standaard links en formulieren naar AJAX-aangedreven interacties. Wanneer ingeschakeld, triggert een klik op een link een fetch-request, de response body vervangt de huidige pagina-inhoud en de browsergeschiedenis wordt bijgewerkt. ```ruby # app/views/layouts/application.html.erb <%= turbo_refreshes_with method: :morph, scroll: :preserve %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> <%= yield %> ``` Het `data-turbo-track: "reload"` attribuut zorgt ervoor dat asset-wijzigingen volledige pagina-herlaadacties triggeren, wat verouderde CSS of JavaScript voorkomt. De `turbo_refreshes_with` helper configureert morphing-gedrag globaal, maar kan per pagina worden overschreven. Turbo Drive uitschakelen voor specifieke links vereist het `data-turbo` attribuut: ```erb <%= link_to "Download PDF", document_path(@doc, format: :pdf), data: { turbo: false } %> <%= link_to "Admin Paneel", admin_path, data: { turbo_action: "replace" } %> ``` ## Turbo Frames: Begrensde Pagina-Updates Turbo Frames isoleren paginasecties in onafhankelijk bij te werken regio's. Een frame omsluit content met een unieke identifier, en elke navigatie binnen dat frame werkt alleen de omsloten content bij. ```erb <%= turbo_frame_tag "comments" do %> <% @comments.each do |comment| %> <%= render comment %> <% end %> <%= link_to "Meer laden", comments_path(page: @page + 1) %> <% end %> ``` De serverresponse moet een overeenkomende frame-tag bevatten. Turbo extraheert de frame-content en vervangt het bestaande frame in de DOM: ```erb <%= turbo_frame_tag "comments" do %> <% @comments.each do |comment| %> <%= render comment %> <% end %> <% if @comments.any? %> <%= link_to "Meer laden", comments_path(page: @page + 1) %> <% end %> <% end %> ``` Lazy loading frames stellen het laden van content uit totdat het frame in de viewport komt: ```erb <%= turbo_frame_tag "user_stats", src: user_stats_path(@user), loading: :lazy do %>
<% end %> ``` ## Turbo Streams: Real-Time DOM-Manipulatie Turbo Streams bieden zeven acties voor het manipuleren van de DOM: `append`, `prepend`, `replace`, `update`, `remove`, `before` en `after`. Elke actie richt zich op een element via ID en past de gespecificeerde wijziging toe. ```erb <%= turbo_stream.prepend "comments" do %> <%= render @comment %> <% end %> <%= turbo_stream.update "comment_count" do %> <%= @comments_count %> reacties <% end %> <%= turbo_stream.replace "new_comment_form" do %> <%= render "form", comment: Comment.new %> <% end %> ``` Controllers reageren op Turbo Stream-requests naast HTML: ```ruby # app/controllers/comments_controller.rb class CommentsController < ApplicationController def create @comment = @post.comments.build(comment_params) respond_to do |format| if @comment.save format.turbo_stream format.html { redirect_to @post, notice: "Reactie toegevoegd" } else format.turbo_stream do render turbo_stream: turbo_stream.replace( "new_comment_form", partial: "form", locals: { comment: @comment } ) end format.html { render :new, status: :unprocessable_entity } end end end end ``` ## Turbo 8 Morphing: Intelligente DOM-Updates Turbo 8 introduceerde [morphing](https://turbo.hotwired.dev/handbook/page_refreshes), aangedreven door de idiomorph-bibliotheek. In plaats van hele paginasecties te vervangen, vergelijkt morphing de huidige DOM met inkomende HTML en past minimale wijzigingen toe. Dit bewaart formulierstatus, scrollpositie en focus. ```ruby # app/models/message.rb class Message < ApplicationRecord belongs_to :conversation broadcasts_refreshes end ``` De `broadcasts_refreshes` callback plant een achtergrondtaak wanneer records wijzigen. De taak broadcast een refresh-signaal naar alle geabonneerde clients via Action Cable. Clients halen de huidige pagina-HTML op en morphen hun DOM om overeen te komen. ```erb <%= turbo_stream_from @conversation %>
<%= render @conversation.messages %>
<%= render "messages/form", conversation: @conversation %> ``` De `turbo_stream_from` helper abonneert de pagina op een Turbo Streams-kanaal. Wanneer een bericht in de conversatie wijzigt, zien alle kijkers direct updates. ### Morphing-Configuratieopties Fijnmazige controle over morphing-gedrag gebruikt data-attributen: ```erb
<%= render "chart", data: @analytics %>
``` De refresh-debouncer voorkomt overmatige broadcasts. Wanneer meerdere database-updates snel achter elkaar plaatsvinden, wordt slechts één refresh getriggerd na een vertraging van 500ms. Dit is configureerbaar per model: ```ruby # app/models/task.rb class Task < ApplicationRecord broadcasts_refreshes debounce: 1.second end ``` ## Interviewvragen: Hotwire-Fundamenten Technische interviews beoordelen vaak Hotwire-kennis door conceptuele en implementatievragen. **V: Hoe verschilt Turbo Drive van traditionele AJAX-navigatiebibliotheken?** Turbo Drive onderschept automatisch alle navigatie zonder expliciete configuratie. Het behoudt het gedrag van de terug/vooruit-knoppen van de browser, werkt de URL bij via pushState en handelt redirects transparant af. Het belangrijkste onderscheid is progressive enhancement: applicaties werken zonder JavaScript en krijgen snelheidsverbeteringen wanneer JavaScript laadt. **V: Wanneer moeten Turbo Frames de voorkeur krijgen boven Turbo Streams?** Turbo Frames zijn geschikt voor navigatie-gebaseerde interacties waarbij een klik op een link een specifieke paginaregio moet bijwerken. Frames behouden URL-semantiek en werken met browsergeschiedenis. Turbo Streams behandelen broadcast-scenario's waarbij meerdere elementen gelijktijdig moeten worden bijgewerkt of waarbij updates afkomstig zijn van achtergrondprocessen in plaats van gebruikersnavigatie. **V: Leg de morphing refresh-cyclus uit in Turbo 8.** Wanneer `broadcasts_refreshes` triggert, plaatst Rails `Turbo::Streams::BroadcastStreamJob` in de wachtrij. De taak broadcast een refresh-actie naar de stream-naam afgeleid van het model. Geabonneerde clients ontvangen de actie, halen de huidige pagina op via GET-request en geven beide DOM's door aan idiomorph. De bibliotheek berekent de minimale diff en past wijzigingen toe, waarbij elementen gemarkeerd met `data-turbo-permanent` behouden blijven. ## Een Real-Time Notificatiesysteem Bouwen Een praktische implementatie demonstreert Hotwire's mogelijkheden voor real-time features: ```ruby # app/models/notification.rb class Notification < ApplicationRecord belongs_to :user after_create_commit :broadcast_to_user private def broadcast_to_user broadcast_prepend_to( "notifications_#{user_id}", target: "notifications", partial: "notifications/notification", locals: { notification: self } ) broadcast_update_to( "notifications_#{user_id}", target: "unread_count", html: user.notifications.unread.count.to_s ) end end ``` De view abonneert zich op het gebruikerspecifieke kanaal: ```erb ``` Wanneer een notificatie ergens in het systeem wordt aangemaakt, wordt de navbar van de gebruiker direct bijgewerkt zonder polling. ## Prestatieoptimalisatiepatronen Productieapplicaties profiteren van verschillende optimalisatietechnieken. ### Eager Loading van Stream-Subscripties Vermijd N+1 subscriptie-patronen in lijsten: ```ruby # app/controllers/projects_controller.rb def index @projects = current_user.projects.includes(:tasks) end ``` ```erb <% @projects.each do |project| %> <%= turbo_stream_from project %> <%= render project %> <% end %> ``` ### Selectieve Broadcasting Beperk broadcasts tot relevante wijzigingen: ```ruby # app/models/post.rb class Post < ApplicationRecord broadcasts_refreshes_to ->(post) { post.published? ? :published_posts : :draft_posts } end ``` ### Caching met Morphing Fragment caching werkt samen met morphing wanneer cache-keys timestamps bevatten: ```erb <% cache [@conversation, @conversation.messages.maximum(:updated_at)] do %> <%= render @conversation.messages %> <% end %> ``` ## Veelvoorkomende Valkuilen en Oplossingen Verschillende problemen komen regelmatig voor bij het implementeren van Hotwire-applicaties. **JavaScript-Initialisatie**: Stimulus-controllers moeten rekening houden met morphing. Gebruik `connect()` en `disconnect()` callbacks correct, en vermijd het opslaan van state die morphing ongeldig zou maken. ```javascript // app/javascript/controllers/chart_controller.js import { Controller } from "@hotwired/stimulus" export default class extends Controller { connect() { this.chart = new Chart(this.element, this.chartConfig) } disconnect() { this.chart?.destroy() } get chartConfig() { return JSON.parse(this.element.dataset.chartConfig) } } ``` **Behoud van Formulierstatus**: Morphing behoudt formulierinvoer standaard. Voor formulieren die moeten worden gereset na verzending, leeg ze expliciet in de Turbo Stream-response: ```erb <%= turbo_stream.replace "message_form" do %> <%= render "form", message: Message.new %> <% end %> ``` **Scrollpositiesprongen**: De `scroll: :preserve` optie helpt, maar complexe layouts hebben mogelijk expliciete scroll-anchoring nodig: ```erb
<%= render @messages %>
``` ## Stimulus-Integratie voor Verbeterde Interactiviteit Terwijl Turbo de meeste interacties afhandelt, vult [Stimulus](https://stimulus.hotwired.dev/) lacunes die client-side logica vereisen. De combinatie houdt JavaScript gefocust en minimaal. ```javascript // app/javascript/controllers/auto_submit_controller.js import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["form"] submit() { clearTimeout(this.timeout) this.timeout = setTimeout(() => { this.formTarget.requestSubmit() }, 300) } } ``` ```erb
<%= form_with model: @search, data: { auto_submit_target: "form" } do |f| %> <%= f.text_field :query, data: { action: "input->auto-submit#submit" }, placeholder: "Zoeken..." %> <% end %>
``` Voor meer geavanceerde patronen, bekijk de modules [caching-strategieën](/technologies/ruby-on-rails/interview-questions/caching-strategies) en [Rails-testbenaderingen](/technologies/ruby-on-rails/interview-questions/rails-testing-rspec). ## Conclusie - Turbo Drive biedt SPA-achtige navigatie zonder configuratie door links en formulieren te onderscheppen - Turbo Frames beperken updates tot paginaregio's, waardoor inline bewerking en lazy loading mogelijk worden - Turbo Streams broadcast DOM-wijzigingen via zeven acties: append, prepend, replace, update, remove, before, after - Turbo 8 morphing met `broadcasts_refreshes` levert real-time updates met idiomorph voor minimale DOM-wijzigingen - Behoud elementen over morphs met `data-turbo-permanent` - Gebruik `turbo_stream_from` in views gekoppeld aan model-callbacks voor broadcasting - De standaard 500ms debounce voorkomt overmatige broadcasts tijdens snelle updates - Stimulus-controllers moeten correcte `connect`/`disconnect` lifecycle-methoden gebruiken voor morphing-compatibiliteit - Fragment caching blijft effectief wanneer cache-keys timestamps bevatten --- 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/rails-turbo-hotwire-2026-real-time-applications