How do we start using native ES2015 / ES6 Custom Elements with Turbolinks?

As custom elements bring their own life-cycle management we need to make sure that our components aren’t re-evaluated by Turbolinks on every new page visit. It’s helpful to facilitate importing and registering our custom elements in their own JavaScript bundle (or “pack” in webpacker tongue). By doing this we can tell Turbolinks to leave elements.js alone and don’t touch it during page transitions:

<%= javascript_pack_tag 'elements', 'data-turbolinks-eval': false, defer: true %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload', defer: true %>

Imagine our elements.js looks like this:

import AlertMessage from './components/alert-message'
customElements.define('alert-message', AlertMessage)
app/javascript/packs/elements.js

In Rails’s default application.js pack we can then import Turbolinks and start it:

import Turbolinks from 'turbolinks'
Turbolinks.start()
app/javascript/packs/application.js

This relies on Turbolinks being available via npm. By integrating Turbolinks via npm you still need the gem for some Rails internals, but you have to disable the automatic require. Otherwise Rails will try to mangle Turbolinks into its own Sprockets-based asset pipeline. We don’t want that for JavaScript bundles as we’re using webpack now.

gem 'turbolinks', require: false
Gemfile

By disabling automatic requiring you’re expected to manually include Turbolinks' redirection helpers in your ApplicationController:

class ApplicationController < ActionController::Base
  include Turbolinks::Redirection
  # ...
end

That’s all you need to get Rails to run smoothly with Turbolinks and a npm-/webpack-based asset pipeline for your JavaScript custom elements. Their native hooks like connectedCallback() and disconnectedCallback() now all perform as expected.