Pawel Janiak

South African Ruby on Rails developer, ranter, biker.

Rails 4, turbolinks and wiselinks

15 May 2013

Turbolinks and Ruby on Rails 4

If you’ve heard about Rails 4, you would have heard about Turbolinks. A quick rundown of it is as follows. One of HTML5’s JavaScript APIs is called the History Interface. This API introduces a pushState that allows data to be inserted into a browser’s session history via JavaScript. With PJAX, which uses this API together with XmlHttpRequests loads only the parts of a page that require updating. This generally excludes assets like CSS and JavaScript files. This means new page requests aren’t bloated with requests for assets and will be faster in most cases.

Turbolinks also uses pushState but doesn’t replace just the parts of a page that need changing. It will load up a whole page and replace the body and title tags (it too does not reload assets). It does this with minimal setup, so if you don’t like magic and specifying which links you’d like turbo’d up then you may have issues with Turbolinks.

Setup

All you do is add the turbolinks gem to your Gemfile, bundle and add:

//= require turbolinks

to your application.js.coffee file. That should be it and if you try out your site in a modern browser you’ll see the requests being made from a JS initiator in your web inspector. To exclude links you’ll have to add a no-turbolink attribute to your links, like so:

<%= link_to 'Home', root_path, 'data-no-turbolink' => true %>

Animations

If you’d like to animate page changes when using turbolinks, you can add the following to your application.js.coffee

    $(document).on 'page:fetch', ->
      $('#content').fadeOut 'slow'

    $(document).on 'page:restore', ->
      $('#content').fadeIn 'slow'

You can also use CSS3 animations too, as outlined in this StackOverflow answer.

Gotchas

Turbolinks is not without its gotchas. In fact it had many, many gotchas and issues, but you’ll see in the Github repo’s issue tracker that the number of open issues have plummeted.

One of the important ones to know about is the use of document.ready. That is because the document.ready event will only fire off when the DOM has completely loaded. When you click a “turbolink”, the DOM doesn’t reload because technically you’re still on the same HTML page and so JavaScript from the newly added content will be waiting for its own document.ready which is never fired off. One solution to this is to move all document.ready dependent code into a seperate function, and then keep it bound to document.ready but additionally bind it to the page:change event.

Numbers

Take a look at this repo by Steve Klabnik about the potential performance improvements you may gain using turbolinks. Add it in your own project in a seperate branch, run your benchmarks and decide if you want to use it or not.

The wise alternative

Igor Alexandrov has a gem called wiselinks, which is like turbolinks but with more capabilities. Unlike turbolinks (at the time of writing), wiselinks processes 30x HTTP redirects, works in browsers that don’t have the History API, and best of all can handle form processing.

Simply add the wiselinks gem to your Gemfile and run bundle install. Then in your app’s application.js.coffee file add a wiselinks object like so (See the Github page for options you can add, like disabling HTML4 browser support):

$(document).ready -> window.wiselinks = new Wiselinks()

Then to add wiselinks to a link, you have to give it a data attribute of push set to true, like so:

<% link_to "Home", root_path, data: { push: true } %>

If you click on the link with your browser’s web inspector open, you will now see the request being initiated via one of your JavaScript files. You’ll also notice the URL change in the address bar thanks to pushStage changing the URL. Wiselinks has a philosophy of only rendering new content that is needed for any request, much like PJAX. Also, it only works on the links and forms you specify, so there is less magic and more control on your part.

Legacy browser support

The nice thing about turbolinks, PJAX, and wiselinks is that they all gracefully degrade to at least normal request processing (as mentioned wiselinks does support browsers without History API support). If you need to support users that are on browsers without JavaScript (say like Opera Mini) then all links will work as regular HTTP requests.