Rails 7.2 came with a feature to check for allowed browsers. This allows you to block older browsers that don’t support certain features of your app (eg. webp images, web push and CSS nesting and so on).
The way it works is essentially a simple before action; it’s added by default for new apps.
class ApplicationController < ActionController::Base
allow_browser versions: :modern
end
This will block all requests (that inherit from ApplicationController) and return a 426 error and render the HTML for it (stored in public/). I find it is written quite elegant, but that’s just an aside.
While this set up might work for some, I want to be a a bit friendlier. I want my apps to be usable for more people or provide good progressive enhancements. So I remove above line from my ApplicationController and implement my own.
This is how it looks:
The features on that page might use some new browser features that are only available in browser from 2017 and up (for example).
How to add this? Easy!
<%= tag.div class: "flex items-center justify-center px-3 py-2 text-sm font-medium rounded gap-x-2 bg-amber-50 text-amber-800" do %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" data-slot="icon" class="shrink-0 size-3 translate-y-px">
<path fill-rule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" clip-rule="evenodd"/>
</svg>
Sorry! Your browser can't run this feature correctly. Try opening it in a modern browser instead.
<% end if unsupported_browser(versions: :modern) %>
That’s the HTML for it. It uses Tailwind CSS, but that’s of course optional. The interesting part is the dangling unsupported_browser
method. Let’s see how that looks.
class ApplicationController < ActionController::Base
# …
helper_method :unsupported_browser
private
def unsupported_browser(versions: :modern)
require "useragent"
ActionController::AllowBrowser::BrowserBlocker.new(request, versions: versions).blocked?
end
end
It uses the same (private) class that powers allow_browser
method. Be aware that it is private so it might be change with each(!) new release. Best to add a little test for it!
And that’s it. Got a better version for this? Insights or ideas? Let me know.