# Ruby on Rails 7: Hotwire i Turbo dla Reaktywnych Aplikacji > Kompletny przewodnik po Hotwire i Turbo w Rails 7. Budowanie reaktywnych aplikacji bez JavaScript z Turbo Drive, Frames i 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 zrewolucjonizowalo tworzenie stron internetowych, integrujac Hotwire domyslnie. Ten stack umozliwia budowanie wysoce reaktywnych aplikacji bez pisania ani jednej linii niestandardowego kodu JavaScript. Turbo Drive, Turbo Frames i Turbo Streams zastepuja tradycyjne podejscia SPA filozofia "HTML po kablu". > **Dlaczego Hotwire?** > > Hotwire drastycznie redukuje zlozonosc frontendu. Nie potrzeba React ani Vue do dynamicznych interfejsow: serwer wysyla gotowy do uzycia HTML, a Turbo automatycznie zajmuje sie aktualizacjami DOM. ## Zrozumienie Architektury Hotwire Hotwire sklada sie z trzech komplementarnych technologii, ktore wspolpracuja, aby zapewnic plynne doswiadczenie uzytkownika bez typowej zlozonosci frameworkow JavaScript. **Turbo Drive** przyspiesza nawigacje, przechwytujac klikniecia w linki i wysylanie formularzy. Zamiast ponownie ladowac cala strone, zastepowana jest tylko zawartosc ``, zachowujac kontekst JavaScript i CSS. **Turbo Frames** rozbijaja strony na niezalezne sekcje. Kazdy frame moze byc aktualizowany oddzielnie, umozliwiajac ukierunkowane interakcje bez wplywu na reszte strony. **Turbo Streams** umozliwiaja aktualizacje w czasie rzeczywistym przez WebSocket lub w odpowiedzi na zadania HTTP. Osiem akcji CRUD jest dostepnych do deklaratywnej manipulacji DOM. ```ruby # Gemfile # Installing Turbo Rails (included by default in Rails 7+) gem 'turbo-rails' ``` Dla istniejacych projektow instalacja wymaga tylko jednego polecenia. ```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" ``` ## Poczatkowa Konfiguracja Turbo Drive Turbo Drive jest domyslnie wlaczone w Rails 7. Cala nawigacja automatycznie staje sie "turbo" bez dodatkowej konfiguracji. Zachowanie mozna dostosowywa za pomoca atrybutow data. ```erb <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %>
<%= yield %> ``` Turbo Drive mozna wylaczyc dla konkretnych linkow lub formularzy, gdy jest to potrzebne. ```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 Turbo Drive** > > Turbo Drive buforuje odwiedzone strony. Atrybut `data-turbo-track="reload"` na zasobach wymusza pelne ponowne ladowanie, jesli pliki CSS/JS ulegna zmianie. ## Turbo Frames dla Ukierunkowanych Aktualizacji Turbo Frames definiuja strefy strony, ktore aktualizuja sie niezaleznie. Kazdy frame ma unikalny identyfikator i reaguje tylko na odpowiedzi zawierajace pasujacy frame. ```erb

Messages

<%= render @messages %>
<%= link_to "Load more", messages_path(page: @next_page) %>
<%= render "form", message: Message.new %> ``` Odpowiedz serwera musi zawierac frame o tym samym identyfikatorze, aby aktualizacja zadzialala. ```erb

<%= message.content %>

<%= link_to "Edit", edit_message_path(message) %> <%= button_to "Delete", message_path(message), method: :delete %>
``` ### Lazy Loading z Turbo Frames Framy moga ladowac swoja zawartosc asynchronicznie za pomoca atrybutu `src`. Poczatkowa zawartosc jest wyswietlana podczas ladowania. ```erb

Dashboard

Loading statistics...

Loading notifications...

<%= render "activity/placeholder" %> ``` Kontroler odpowiada widokiem zawierajacym tylko zadany frame. ```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 dla Aktualizacji w Czasie Rzeczywistym Turbo Streams oferuje osiem akcji do manipulacji DOM: `append`, `prepend`, `replace`, `update`, `remove`, `before`, `after` i `morph`. Te akcje moga byc wyzwalane przez odpowiedz HTTP lub 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 ``` ### Dedykowane Szablony Turbo Stream Dla bardziej zlozonych odpowiedzi szablon `.turbo_stream.erb` oferuje wieksza elastycznosc. ```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 %> ``` > **Format Odpowiedzi** > > Odpowiedzi Turbo Stream musza miec content-type `text/vnd.turbo-stream.html`. Rails obsluguje to automatycznie za pomoca `respond_to` i formatu `turbo_stream`. ## Broadcasting w Czasie Rzeczywistym z Action Cable Turbo Streams naprawde blyszczy przy broadcastingu przez WebSocket. Uzytkownicy otrzymuja aktualizacje natychmiast bez pollingu. ```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 ``` Widok subskrybuje odpowiedni stream za pomoca helpera `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 %> ``` ### Asynchroniczny Broadcasting dla Ciezkich Operacji Aby uniknac blokowania zadan, broadcasting moze byc wykonywany w tle. ```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 ``` ## Zaawansowane Wzorce z Turbo ### Edycja Formularzy Inline Czesty wzorzec polega na zastepowaniu statycznej zawartosci formularzem edycji inline. ```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 %>
``` ### Nawigacja Poza Ramka Nadrzedna Domyslnie linki w ramce pozostaja wewnatrz tej ramki. Atrybut `data-turbo-frame` umozliwia wskazanie innej ramki lub calej strony. ```erb ``` ```erb
<%= yield %>
``` > **Debugowanie Turbo** > > W srodowisku deweloperskim otworz konsole JavaScript i wpisz `Turbo.setProgressBarDelay(0)`, aby natychmiast zobaczyc pasek postepu. `Turbo.session.drive = false` tymczasowo wylacza Turbo Drive. ## Obsluga Bledow i Stany Ladowania Dobry UX wymaga obslugi stanow ladowania i bledow sieciowych. ```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") } } ``` ### Obsluga Bledow Serwera ```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 ``` ## Optymalizacja Wydajnosci Kilka technik zapewnia wydajne aplikacje Turbo. ```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 %> ``` ## Podsumowanie Hotwire i Turbo transformuja rozwoj w Rails, eliminujac zlozonosc tradycyjnych frameworkow JavaScript. Turbo Drive przyspiesza nawigacje, Turbo Frames umozliwiaja ukierunkowane aktualizacje, a Turbo Streams oferuje potezne mozliwosci w czasie rzeczywistym. ### Lista Kontrolna na Start z Hotwire - Uzywac Rails 7+ dla wbudowanej integracji z Hotwire - Zrozumiec trzy komponenty: Drive, Frames i Streams - Zidentyfikowac strefy strony, ktore skorzystaja na Turbo Frames - Uzywac `respond_to` z `format.turbo_stream` w kontrolerach - Wdrozyc broadcasting dla funkcji czasu rzeczywistego - Polaczyc ze Stimulus dla prostych interakcji JavaScript To podejscie "HTML po kablu" umozliwia budowanie nowoczesnych, reaktywnych aplikacji przy zachowaniu prostoty i produktywnosci, ktore czynia Ruby on Rails tak poteznym. Rezultat: mniej kodu do utrzymania, doskonala wydajnosc i optymalne doswiadczenie deweloperskie. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/ruby-on-rails/ruby-on-rails-7-hotwire-turbo