Use Browser-sync for Rails 7 with Hotwire Turbo
Rails 7 with Hotwire Turbo is awesome. However, when it comes to fine-tune layouts and views, especially with Tailwind CSS, live-reload becomes a must-have feature.
I used to import the hotwire-livereload
gem to achieve that. It’s a recently published gem for Hotwire Rails applications.
But it may break the app when:
-
No available Redis server. The gem has declared its Redis dependency but never check it at the initialization. Instead, it just breaks the server thread with no exception handler.
-
Live-reload disabled. Unlike the
rack-mini-profiler
gem, this gem requires to insert the tag and handle with disabling like this:<% if Rails.env.development? && !Rails.root.join('tmp/livereload-disabled.txt').exist? %> <%= hotwire_livereload_tags %> <% end %>
-
Previewing view components. It just breaks.
Browser-sync to save life
By contrast, Browsersync is a much more mature solution for web page live reload and even for cross-device browser testing!
It only takes seconds to setup for Rails apps with Hotwire Turbo.
0. Install and initialize
Install browser-sync as a global dependency and to use a bs-config.js
for configurations.
npm install --global browser-sync
browser-sync init
1. Setup X-Forwarded-Host
header for authenticity check
Rails checks the authenticity token when submitting forms for security reason. Thus, we must setup the X-Forwarded-Host
header to make the proxy totally work:
proxy: {
target: 'localhost:3000',
proxyReq: [
// Setup X-Forwarded-Host header for authenticity token check.
(proxyReq) => proxyReq.setHeader('X-Forwarded-Host', 'localhost:3001')
]
}
2. Setup snippetOptions
for Turbo Drive
Turbo Drive replace the content of <body>
upon each response. Thus, we must place the browser-sync snippet at the of <head>
part:
snippetOptions: {
rule: {
match: /<\/head>/i,
fn: function (snippet, match) {
return snippet + match
}
}
}
3. Setup files
to be watched
files: [
'app/assets/images/**/*',
'app/assets/stylesheets/**/*',
'app/components/**/*',
'app/helpers/**/*',
'app/javascript/**/*',
'app/views/**/*',
'config/locales/**/*'
]
Wrap it up
Here is the full content of the bs-config.js
file. Run by: browser-sync start -c bs-config.js
.
module.exports = {
files: [
'app/assets/images/**/*',
'app/assets/stylesheets/**/*',
'app/components/**/*',
'app/helpers/**/*',
'app/javascript/**/*',
'app/views/**/*',
'config/locales/**/*'
],
proxy: {
target: 'localhost:3000',
proxyReq: [
// Setup X-Forwarded-Host header for authenticity token check.
(proxyReq) => proxyReq.setHeader('X-Forwarded-Host', 'localhost:3001')
]
},
port: 3001,
// Use a custom rule to make compatible with Hotwire Turbo.
snippetOptions: {
rule: {
match: /<\/head>/i,
fn: function (snippet, match) {
return snippet + match
}
}
},
// Stop the browser from automatically opening.
open: false,
// Don't show any notifications in the browser.
notify: false
}