Introduction
Turbo makes Rails applications feel as fast as single-page apps by avoiding full page reloads. It intercepts link clicks and form submissions, replacing only the parts of the page that changed.
Key Concepts
- Turbo Drive: Automatically intercepts all navigation and uses AJAX to swap only the
<body>, preserving the<head>and avoiding full page reloads. - Turbo Frames: Scoped regions of a page that update independently — clicking a link inside a frame only updates that frame.
- Turbo Streams: Server-sent HTML fragments that append, prepend, replace, or remove elements on the page in real time.
Real World Context
A product listing with pagination, filters, and search can feel instant with Turbo Frames: clicking page 2 only replaces the product grid, not the header, sidebar, or footer. Users perceive near-zero load times.
Deep Dive
Turbo Drive (Default)
erb<!-- Regular link — handled by Turbo automatically --> <%= link_to 'Products', products_path %> <!-- Disable Turbo for specific link --> <%= link_to 'Download', file_path, data: { turbo: false } %>
Turbo Frames for Partial Updates
erb<!-- Only this frame updates on navigation --> <%= turbo_frame_tag 'product_list' do %> <%= render @products %> <%= link_to 'Next', products_path(page: 2) %> <% end %> <!-- Lazy load content --> <%= turbo_frame_tag 'comments', src: post_comments_path(@post), loading: :lazy do %> <p>Loading comments...</p> <% end %>
Turbo Streams for Live Updates
rubydef create @comment = @post.comments.create!(comment_params) respond_to do |format| format.turbo_stream format.html { redirect_to @post } end end
erb<!-- app/views/comments/create.turbo_stream.erb --> <%= turbo_stream.append 'comments', @comment %> <%= turbo_stream.update 'comment_count', @post.comments.count %>
Prefetching on Hover
erb<%= link_to 'Products', products_path, data: { turbo_prefetch: true } %>
Common Pitfalls
- Breaking out of frames accidentally — Links inside a Turbo Frame stay within the frame by default. To navigate the full page, add
data-turbo-frame="_top". - Not providing fallback for non-Turbo requests — Always handle both
format.turbo_streamandformat.htmlin your respond_to block.
Best Practices
- Wrap pagination in Turbo Frames — Users see instant page transitions without full reloads.
- Use lazy-loading frames for below-the-fold content — Defer loading comments, related posts, or sidebars until the user needs them.
Summary
- Turbo Drive intercepts all navigation automatically — no code needed.
- Turbo Frames scope updates to specific regions of the page.
- Turbo Streams enable real-time append/replace/remove operations.
- Use
loading: :lazyon Turbo Frames to defer below-the-fold content.
Code Examples
ruby
# Lazy-loaded Turbo Frame — content loads only when visible
<%= turbo_frame_tag 'product_reviews',
src: product_reviews_path(@product),
loading: :lazy do %>
<p>Loading reviews...</p>
<% end %>
# Server responds with just the frame content
# No full page render needed — only the reviews HTML