Introducing Turbolinks for Rails 4.0
David Heinemeier Hansson, creator of Ruby on Rails, recently announced on Twitter that a feature called Turbolinks will ship as default-on in the Gemfile of Ruby on Rails 4.0. Turbolinks is already powering the mobile website of Basecamp.
Someone at Hacker News posted a link to the Turbolinks repository on Github and titled it:
Turbolinks for Rails (like pjax)
So what is PJAX and what does Turbolinks do differently?
JavaScript pushState
HTML5 introduced several new APIs to JavaScript, one being the History interface. pushState
allows JavaScript to store arbitrary data into the session history, combined with a title and an optional URL. The back()
and forward()
methods then allow you to navigate through the pushed session history. This way pushState
can be used to store the navigation history of the current page and dynamically switch backwards on forwards through different states without reloading the whole page.
PJAX
PJAX is a portmanteau of the terms pushState
and AJAX
. The idea behind PJAX is simple: Instead of reloading the whole page when clicking a link, just load the part of the page that needs updating. This obviously requires JavaScript on the client side to send an AJAX request to the server, fetch the data and replace the component of the page that needs to be updated.
The above sketch shows how PJAX works in practice. First (1) a normal GET request is sent to the server, which returns a complete page (2). All consecutive requests (3) triggered by clicking on a link will only fetch part of a page (4).
On the client-side you define the container (#main
in this case) that should be replaced when a link to /authors
is clicked.
$.pjax({
url: '/authors',
container: '#main'
})
PJAX also requires the server-side application to only return the selected part of the page. For this reason PJAX adds a HTTP header called X-PJAX
to the request, to let the web-application know that the request is coming from PJAX. In Rails you can simply check for that header and set render :layout => false
.
if request.headers['X-PJAX']
render :layout => false
end
There are other ways of using PJAX in Rails, most of which have been described by Ryan Bates in Railscasts Episode #294. PJAX has its downsides in that you carefully have to think about which parts of a page to replace.
Turbolinks
Turbolinks also uses pushState
, but instead of replacing only parts a page it loads a complete website from the server and replaces the <title>
and <body>
in the currently loaded DOM. By default it applies this to all links on a page. So unlike PJAX you don't have to mark links and containers to support in place reloading, Turbolinks will handle that for you.
Right now, Turbolinks is in a very early stage of development, but as mentioned before, already in use at Basecamp. To use it in your Ruby (Sinatra, Rails, ..) project simply add the turbolinks gem to you app:
gem 'turbolinks'
Do not forget to run bundle install
after editing the Gemfile to install the gem. You will then need to add the turbolinks.js.coffee
file to the asset pipeline by adding a line in app/assets/javascripts/application.js
:
//= require turbolinks
From now on all requests made by a browser that supports pushState
will be handled by turbolinks
. Browsers that have no support for pushState
will still work, as they just fallback to the default link action. You can check if everything is working by opening the developer tools in your browser (hit F12 in Chrome) and opening the Network tab. You should see that turbolink.js
is handling the requests when you click on a link.
If you want to manually exclude links from being handled by Turbolinks you can add the data-no-turbolink
attribute to the link tag:
<a href="/articles" data-no-turbolink>Articles</a>
This will tell Turbolinks to ignore this link. You can apply this attribute to any container up to <body>
and Turbolinks will ignore all links inside that container. One problem arises when you use document.ready
in JavaScript, that event only is fired when the DOM has finished loading, but will not be triggered when Turbolinks performs a page change. A quick-fix for this would be to extract all code tied to document.ready
into a seperate function and bind it to both document.ready
and the page:change
event:
$(function() {
initPage();
});
$(window).bind('page:change', function() {
initPage();
})
Note: Turbolinks is written in CoffeeScript and will therefore require the coffeescript-rails
gem in order to be compiled to javascript. Make sure that this gem is added in your application. If you do not want to use CoffeeScript in your application you can manually download and compile the turbolinks.js.coffee
to JavaScript. Have a look at the CoffeeScript documentation on how to install CoffeeScript and then compile the file by executing coffee -c turbolinks.js.coffee
which will compile the file to turbolinks.js
.
Conclusion
The user JuDue asked an interesting question on Hacker News:
[..] the inevitable vs PJAX conversation (ie: is it flexible enough? Does it need to be?)
As so often the answer is: It depends. Turbolinks definitely improved client-side page loading. The problem is, that the server still has to render the complete website. If that is a bottleneck in your application then Turbolinks will not help. PJAX on the other side can certainlysolve this by rendering only those parts of a website that really need to be updated, but at the cost of additional work when developing the application.
As for flexibility, it highly depends on your needs. If you only have a few links where you want to exclude Turbolinks then it should be no problem. If you serve large websites with a lot of page content you might want to stick to PJAX to update only small parts of your page and keep the network traffic to a minimum.
To wrap things up: Turbolinks will improve your page load significantly if your pages share JavaScript and CSS styling. PJAX comes in handy, when server-side performance is an issue.
Other Resources
- Turbolinks project
- Rails 4 Release Notes (Work in Progress)
- PJAX Demo (Source Code)