Convert Figma logo to code with AI

hotwired logoturbo-rails

Use Turbo in your Ruby on Rails app

2,070
319
2,070
122

Top Related Projects

Build reactive applications with the Rails tooling you already know and love.

6,204

Inertia.js lets you quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers.

22,155

A full-stack framework for Laravel that takes the pain out of building dynamic UIs.

Rich, real-time user experiences with server-rendered HTML

27,910

A rugged, minimal framework for composing JavaScript behavior in your markup.

Quick Overview

Turbo-rails is a Ruby on Rails integration for Hotwire's Turbo framework. It provides seamless integration of Turbo's SPA-like navigation and form handling capabilities into Rails applications, allowing developers to build responsive and dynamic web applications with minimal JavaScript.

Pros

  • Simplifies the creation of modern, responsive web applications without heavy JavaScript frameworks
  • Seamless integration with Ruby on Rails, leveraging existing Rails conventions and helpers
  • Improves application performance by reducing full page reloads
  • Easy to adopt incrementally in existing Rails projects

Cons

  • Learning curve for developers new to Hotwire and Turbo concepts
  • Limited customization options compared to full-fledged JavaScript frameworks
  • Potential compatibility issues with certain JavaScript libraries or complex DOM manipulations
  • May require additional configuration for more advanced use cases

Code Examples

  1. Turbo Frame for partial page updates:
<%= turbo_frame_tag "user_list" do %>
  <% @users.each do |user| %>
    <%= render partial: "user", locals: { user: user } %>
  <% end %>
<% end %>
  1. Turbo Stream for real-time updates:
class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      respond_to do |format|
        format.turbo_stream { render turbo_stream: turbo_stream.append("user_list", partial: "user", locals: { user: @user }) }
      end
    end
  end
end
  1. Turbo Drive for form submissions:
<%= form_with(model: @post, data: { turbo: true }) do |form| %>
  <%= form.text_field :title %>
  <%= form.text_area :content %>
  <%= form.submit %>
<% end %>

Getting Started

  1. Add turbo-rails to your Gemfile:
gem 'turbo-rails'
  1. Run bundle install:
bundle install
  1. Install Turbo in your Rails application:
rails turbo:install
  1. Include Turbo in your JavaScript pack:
// app/javascript/application.js
import "@hotwired/turbo-rails"
  1. Ensure Turbo is loaded in your layout:
<!-- app/views/layouts/application.html.erb -->
<%= javascript_importmap_tags %>

Now you can start using Turbo features in your Rails application!

Competitor Comparisons

Build reactive applications with the Rails tooling you already know and love.

Pros of Stimulus Reflex

  • More fine-grained control over DOM updates
  • Allows for real-time updates without full page reloads
  • Supports complex interactions with less server-side code

Cons of Stimulus Reflex

  • Steeper learning curve due to its unique programming model
  • Requires more client-side JavaScript
  • Can be more challenging to debug compared to traditional Rails approaches

Code Comparison

Stimulus Reflex:

class ExampleReflex < ApplicationReflex
  def increment
    @count = element.dataset[:count].to_i + 1
  end
end

Turbo Rails:

class ExamplesController < ApplicationController
  def increment
    @count = params[:count].to_i + 1
    render turbo_stream: turbo_stream.update("count", @count)
  end
end

Stimulus Reflex allows for more direct manipulation of the DOM from the server-side, while Turbo Rails follows a more traditional controller-based approach with the added benefit of Turbo Streams for partial page updates.

Both libraries aim to enhance the responsiveness of Rails applications, but they take different approaches. Stimulus Reflex offers a more reactive, real-time feel, while Turbo Rails builds upon the familiar Rails conventions with added interactivity.

6,204

Inertia.js lets you quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers.

Pros of Inertia

  • Allows using server-side routing and controllers with client-side rendering
  • Supports multiple frontend frameworks (React, Vue, Svelte)
  • Simplifies state management by leveraging server-side data

Cons of Inertia

  • Requires more setup and configuration compared to Turbo Rails
  • May have a steeper learning curve for developers new to SPA concepts
  • Less seamless integration with Rails ecosystem

Code Comparison

Turbo Rails (Stimulus controller):

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    this.element.textContent = "Hello World!"
  }
}

Inertia (Vue component):

<template>
  <div>{{ message }}</div>
</template>

<script>
export default {
  data() {
    return { message: 'Hello World!' }
  }
}
</script>

Both Turbo Rails and Inertia aim to simplify the development of modern web applications, but they take different approaches. Turbo Rails focuses on enhancing traditional server-rendered Rails applications with JavaScript sprinkles, while Inertia bridges the gap between server-side and client-side rendering, allowing developers to build single-page applications using familiar server-side patterns.

22,155

A full-stack framework for Laravel that takes the pain out of building dynamic UIs.

Pros of Livewire

  • Full-stack framework with tighter PHP integration
  • Simpler learning curve for PHP developers
  • More fine-grained control over component updates

Cons of Livewire

  • Limited to PHP/Laravel ecosystem
  • Potentially higher server load due to more frequent requests
  • Less flexibility for complex JavaScript interactions

Code Comparison

Livewire component:

class SearchUsers extends Component
{
    public $search = '';

    public function render()
    {
        return view('livewire.search-users', [
            'users' => User::where('name', 'like', "%{$this->search}%")->get(),
        ]);
    }
}

Turbo Rails controller:

class UsersController < ApplicationController
  def index
    @users = User.where("name LIKE ?", "%#{params[:search]}%")
    render partial: "users/list", locals: { users: @users }
  end
end

Both Livewire and Turbo Rails aim to simplify the development of interactive web applications. Livewire offers a more PHP-centric approach, making it easier for Laravel developers to create dynamic interfaces without extensive JavaScript knowledge. Turbo Rails, on the other hand, provides a more lightweight solution that integrates well with existing Rails applications and offers better performance for larger-scale projects. The choice between the two depends on the specific project requirements, team expertise, and desired level of control over the frontend interactions.

Rich, real-time user experiences with server-rendered HTML

Pros of Phoenix LiveView

  • Built-in real-time capabilities with WebSockets
  • Seamless server-side rendering and client-side interactivity
  • Lower JavaScript payload, reducing client-side complexity

Cons of Phoenix LiveView

  • Steeper learning curve for developers new to Elixir/Phoenix
  • Limited ecosystem compared to Ruby on Rails
  • Potential performance issues with complex, stateful UIs

Code Comparison

Phoenix LiveView:

defmodule MyAppWeb.CounterLive do
  use Phoenix.LiveView

  def render(assigns) do
    ~L"""
    <div>
      <h1>Count: <%= @count %></h1>
      <button phx-click="increment">+</button>
    </div>
    """
  end

  def mount(_params, _session, socket) do
    {:ok, assign(socket, count: 0)}
  end

  def handle_event("increment", _, socket) do
    {:noreply, update(socket, :count, &(&1 + 1))}
  end
end

Turbo Rails:

# app/controllers/counter_controller.rb
class CounterController < ApplicationController
  def index
    @count = 0
  end

  def increment
    @count = params[:count].to_i + 1
  end
end

# app/views/counter/index.html.erb
<div>
  <h1>Count: <span id="count"><%= @count %></span></h1>
  <%= button_to "+", increment_counter_path, method: :post, data: { turbo_stream: true } %>
</div>

# app/views/counter/increment.turbo_stream.erb
<%= turbo_stream.update "count", @count %>
27,910

A rugged, minimal framework for composing JavaScript behavior in your markup.

Pros of Alpine

  • Lightweight and minimal, with a small learning curve
  • Framework-agnostic, can be easily integrated into existing projects
  • Doesn't require a build step, works directly in the browser

Cons of Alpine

  • Less powerful for complex, full-stack applications
  • Limited ecosystem and community support compared to Turbo Rails
  • Lacks built-in server-side rendering capabilities

Code Comparison

Alpine:

<div x-data="{ open: false }">
    <button @click="open = !open">Toggle</button>
    <div x-show="open">Content</div>
</div>

Turbo Rails:

<%= turbo_frame_tag "toggle_content" do %>
  <%= button_to "Toggle", toggle_path, method: :post %>
  <div id="content" style="display: none;">Content</div>
<% end %>

Alpine focuses on declarative, client-side interactivity, while Turbo Rails leverages server-side processing and partial page updates. Alpine's syntax is more compact and self-contained, whereas Turbo Rails integrates tightly with Ruby on Rails conventions and server-side logic.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Turbo Turbo

Turbo gives you the speed of a single-page web application without having to write any JavaScript. Turbo accelerates links and form submissions without requiring you to change your server-side generated HTML. It lets you carve up a page into independent frames, which can be lazy-loaded and operate as independent components. And finally, helps you make partial page updates using just HTML and a set of CRUD-like container tags. These three techniques reduce the amount of custom JavaScript that many web applications need to write by an order of magnitude. And for the few dynamic bits that are left, you're invited to finish the job with Stimulus.

On top of accelerating web applications, Turbo was built from the ground-up to form the foundation of hybrid native applications. Write the navigational shell of your Android or iOS app using the standard platform tooling, then seamlessly fill in features from the web, following native navigation patterns. Not every mobile screen needs to be written in Swift or Kotlin to feel native. With Turbo, you spend less time wrangling JSON, waiting on app stores to approve updates, or reimplementing features you've already created in HTML.

Turbo is a language-agnostic framework written in JavaScript, but this gem builds on top of those basics to make the integration with Rails as smooth as possible. You can deliver turbo updates via model callbacks over Action Cable, respond to controller actions with native navigation or standard redirects, and render turbo frames with helpers and layout-free responses.

Navigate with Turbo Drive

Turbo is a continuation of the ideas from the previous Turbolinks framework, and the heart of that past approach lives on as Turbo Drive. When installed, Turbo automatically intercepts all clicks on <a href> links to the same domain. When you click an eligible link, Turbo prevents the browser from following it. Instead, Turbo changes the browser’s URL using the History API, requests the new page using fetch, and then renders the HTML response.

During rendering, Turbo replaces the current <body> element outright and merges the contents of the <head> element. The JavaScript window and document objects, and the <html> element, persist from one rendering to the next.

Whereas Turbolinks previously just dealt with links, Turbo can now also process form submissions and responses. This means the entire flow in the web application is wrapped into Turbo, making all the parts fast. No more need for data-remote=true.

Turbo Drive can be disabled on a per-element basis by annotating the element or any of its ancestors with data-turbo="false". If you want Turbo Drive to be disabled by default, then you can adjust your import like this:

import "@hotwired/turbo-rails"
Turbo.session.drive = false

Then you can use data-turbo="true" to enable Drive on a per-element basis.

See documentation.

Decompose with Turbo Frames

Turbo reinvents the old HTML technique of frames without any of the drawbacks that lead to developers abandoning it. With Turbo Frames, you can treat a subset of the page as its own component, where links and form submissions replace only that part. This removes an entire class of problems around partial interactivity that before would have required custom JavaScript.

It also makes it dead easy to carve a single page into smaller pieces that can all live on their own cache timeline. While the bulk of the page might easily be cached between users, a small personalized toolbar perhaps cannot. With Turbo::Frames, you can designate the toolbar as a frame, which will be lazy-loaded automatically by the publicly-cached root page. This means simpler pages, easier caching schemes with fewer dependent keys, and all without needing to write a lick of custom JavaScript.

This gem provides a turbo_frame_tag helper to create those frames.

For instance:

<%# app/views/todos/show.html.erb %>
<%= turbo_frame_tag @todo do %>
  <p><%= @todo.description %></p>

  <%= link_to 'Edit this todo', edit_todo_path(@todo) %>
<% end %>

<%# app/views/todos/edit.html.erb %>
<%= turbo_frame_tag @todo do %>
  <%= render "form" %>

  <%= link_to 'Cancel', todo_path(@todo) %>
<% end %>

When the user clicks on the Edit this todo link, as a direct response to this user interaction, the turbo frame will be automatically replaced with the one in the edit.html.erb page.

See documentation.

A note on custom layouts

In order to render turbo frame requests without the application layout, Turbo registers a custom layout method. If your application uses custom layout resolution, you have to make sure to return "turbo_rails/frame" (or false for TurboRails < 1.4.0) for turbo frame requests:

layout :custom_layout

def custom_layout
  return "turbo_rails/frame" if turbo_frame_request?

  # ... your custom layout logic

If you are using a custom, but "static" layout,

layout "some_static_layout"

you have to change it to a layout method in order to conditionally return "turbo_rails/frame" for turbo frame requests:

layout :custom_layout

def custom_layout
  return "turbo_rails/frame" if turbo_frame_request?

  "some_static_layout"

Come Alive with Turbo Streams

Partial page updates that are delivered asynchronously over a web socket connection is the hallmark of modern, reactive web applications. With Turbo Streams, you can get all of that modern goodness using the existing server-side HTML you're already rendering to deliver the first page load. With a set of simple CRUD container tags, you can send HTML fragments over the web socket (or in response to direct interactions), and see the page change in response to new data. Again, no need to construct an entirely separate API, no need to wrangle JSON, no need to reimplement the HTML construction in JavaScript. Take the HTML you're already making, wrap it in an update tag, and, voila, your page comes alive.

With this Rails integration, you can create these asynchronous updates directly in response to your model changes. Turbo uses Active Jobs to provide asynchronous partial rendering and Action Cable to deliver those updates to subscribers.

This gem provides a turbo_stream_from helper to create a turbo stream.

<%# app/views/todos/show.html.erb %>
<%= turbo_stream_from dom_id(@todo) %>

<%# Rest of show here %>

See documentation.

Installation

This gem is automatically configured for applications made with Rails 7+ (unless --skip-hotwire is passed to the generator). But if you're on Rails 6, you can install it manually:

  1. Add the turbo-rails gem to your Gemfile: gem 'turbo-rails'
  2. Run ./bin/bundle install
  3. Run ./bin/rails turbo:install
  4. Run ./bin/rails turbo:install:redis to change the development Action Cable adapter from Async (the default one) to Redis. The Async adapter does not support Turbo Stream broadcasting.

Running turbo:install will install through NPM or Bun if a JavaScript runtime is used in the application. Otherwise the asset pipeline version is used. To use the asset pipeline version, you must have importmap-rails installed first and listed higher in the Gemfile.

If you're using node and need to use the cable consumer, you can import cable (import { cable } from "@hotwired/turbo-rails"), but ensure that your application actually uses the members it imports when using this style (see turbo-rails#48).

The Turbo instance is automatically assigned to window.Turbo upon import:

import "@hotwired/turbo-rails"

Usage

You can watch the video introduction to Hotwire, which focuses extensively on demonstrating Turbo in a Rails demo. Then you should familiarize yourself with Turbo handbook to understand Drive, Frames, and Streams in-depth. Finally, dive into the code documentation by starting with Turbo::FramesHelper, Turbo::StreamsHelper, Turbo::Streams::TagBuilder, and Turbo::Broadcastable.

RubyDoc Documentation

For the API documentation covering this gem's classes and packages, visit the RubyDoc page. Note that this documentation is updated automatically from the main branch, so it may contain features that are not released yet.

Compatibility with Rails UJS

Turbo can coexist with Rails UJS, but you need to take a series of upgrade steps to make it happen. See the upgrading guide.

Testing

The Turbo::TestAssertions concern provides Turbo Stream test helpers that assert the presence or absence ofs s <turbo-stream> elements in a rendered fragment of HTML. Turbo::TestAssertions are automatically included in ActiveSupport::TestCase and depend on the presence of rails-dom-testing assertions.

The Turbo::TestAssertions::IntegrationTestAssertions are built on top of Turbo::TestAssertions, and add support for passing a status: keyword. They are automatically included in ActionDispatch::IntegrationTest.

The Turbo::Broadcastable::TestHelper concern provides Action Cable-aware test helpers that assert that <turbo-stream> elements were or were not broadcast over Action Cable. Turbo::Broadcastable::TestHelper is automatically included in ActiveSupport::TestCase.

Rendering Outside of a Request

Turbo utilizes ActionController::Renderer to render templates and partials outside the context of the request-response cycle. If you need to render a Turbo-aware template, partial, or component, use ActionController::Renderer:

ApplicationController.renderer.render template: "posts/show", assigns: { post: Post.first } # => "<html>…"
PostsController.renderer.render :show, assigns: { post: Post.first } # => "<html>…"

As a shortcut, you can also call render directly on the controller class itself:

ApplicationController.render template: "posts/show", assigns: { post: Post.first } # => "<html>…"
PostsController.render :show, assigns: { post: Post.first } # => "<html>…"

Development

Run the tests with ./bin/test.

Using local Turbo version

Often you might want to test changes made locally to Turbo lib itself. To package your local development version of Turbo you can use yarn link feature:

cd <local-turbo-dir>
yarn link

cd <local-turbo-rails-dir>
yarn link @hotwired/turbo

# Build the JS distribution files...
yarn build
# ...and commit the changes

Now you can reference your version of turbo-rails in your Rails projects packaged with your local version of Turbo.

Contributing

Having a way to reproduce your issue will help people confirm, investigate, and ultimately fix your issue. You can do this by providing an executable test case. To make this process easier, we have prepared an executable bug report Rails application for you to use as a starting point.

This template includes the boilerplate code to set up a System Test case. Copy the content of the template into a .rb file and make the necessary changes to demonstrate the issue. You can execute it by running ruby the_file.rb in your terminal. If all goes well, you should see your test case failing.

You can then share your executable test case as a gist or paste the content into the issue description.

License

Turbo is released under the MIT License.

NPM DownloadsLast 30 Days