Some time ago I used Turbolinks in a project and it was instant love. After years working on single page apps with different Javascript frameworks, Turbolinks clicked. And clicked deeply.

Turbolinks is included as part of the default Rails stack. The idea is simple: speed up your application by replacing full page loads with ajax-powered requests. The internal sorcery to make this approach work is hidden, and the developer can focus on a traditional server-side flow, where full-page html is rendered as response to http requests.

When used in conjunction with Rails, the integration is pretty seamless. Just include the gem and:

  • Turbolinks intercepts clicked links to load them faster.
  • When using redirect_to in a controller, for Ajax requests that are not get, it will return a fragment of JS that will use Turbolinks API to handle the redirection on the client.

However, I noticed a consistency problem with forms. When a model backing a form fails to save, it is a common Rails pattern to render the form as part of the response. That way you can reuse the form code and rely on the validated model to display the errors. However, this pattern won’t work with Turbolinks.

To be accurate, what Rails does not support is using render to replace the page html in response to Ajax requests. Considering that forms are remote by default since Rails 5, I think this behavior should be the default: when an Ajax (not get) request renders html content, that should replace the page content as the response (in the general case).

The recommendation is to respond with a server-generated Javascript response that replaces the updated form in the dom.

After using that pattern for a while I noticed a bit of boilerplate repetition, so I extracted a partial to be used like this:

# views/shared/replace_dom.js.erb
$("<%= source_selector %>").replaceWith("<%= j render(template) %>"); 

# in controller
render 'shared/replace_dom', locals: {source_selector: 'some.selector.to.replace', template: 'template_to_render'}

But it still felt very un-Rails-ish to me. I just wanted to use render and not having to care about anything else.

So I went ahead and published turbolinks_render, a gem that adds support for render in Rails controllers with Turbolinks. By default, it will handle all the Ajax requests that are not get and that are not rendering json.

I believe that Turbolinks should support this as part of its official Rails integration, so I opened an issue to discuss its inclusion.