The standard way to configure target browsers with Node.js is Browserslist. It is possible to add the following:
{
"browserslist": [
"last 2 version",
"not dead"
]
}
to the package.json
or the .browserslistrc
config file:
# Browsers that we support
last 2 version
not dead
Those two similar examples mean that the target browsers are two last version and the browser is not dead.
This config is used by many front-end tools, such as Autoprefixer, Babel and many others.
But in this article I am going to write about the Browserslist Useragent frontend tool for finding if a given user agent string satisfies a Browserslist
browsers:
Install the browserslist-useragent
:
npm install browserslist-useragent
and you can determine by User-Agent
string if you browser matches:
const { matchesUA } = require('browserslist-useragent')
matchesUA(userAgentString, options)
// with browserslist config inferred
matchesUA('Mozilla/5.0 (Windows NT 10.0; rv:54.0) Gecko/20100101 Firefox/54.0')
//returns boolean
// with explicit browserslist
matchesUA('Mozilla/5.0 (Windows NT 10.0; rv:54.0) Gecko/20100101 Firefox/54.0', { browsers: ['Firefox > 53']})
// returns true
Imaging we have the .browserslistrc
config file like this:
last 2 versions
not IE 11
not ExplorerMobile 11
not last 1 OperaMini version
not OperaMobile 12.1
not dead
We can get the array of detailed browsers rules with the help of browserslist:
const browserslist = require('browserslist')
const fs = require('fs')
fs.writeFileSync('./browsers.json', JSON.stringify(browserslist()))
For the sample above it will produce the json file with:
[
"and_chr 67",
"and_ff 60",
"and_qq 1.2",
"and_uc 11.8",
"android 67",
"android 4.4.3-4.4.4",
"baidu 7.12",
"chrome 69",
"chrome 68",
"edge 17",
"edge 16",
"firefox 62",
"firefox 61",
"ios_saf 11.3-11.4",
"ios_saf 11.0-11.2",
"op_mob 46",
"opera 55",
"opera 54",
"safari 11.1",
"safari 11",
"samsung 7.2",
"samsung 6.2"
]
That is the way to determine the browsers matchings with Node.js.
Why do we need to check the browsers version on both sides: backend and frontend?
In the case of your modern javascript frontend would not be loaded on the old browser - we can still use the backend rendering to write some HTML notifying user about the issue:
❗This HTML block would work in any browser no matter how old it is.
And if your backend is written with Ruby - use can still use the port of the original tool to the Ruby - browserslist-useragent gem. It works the same way its Node.js version - recognises the family and the version from the User-Agent
header string and matches it with the browserslist
-rules produced by the Browserslists
tool.
Single project
The usage is straightforward - it just needs you to generate the browsers.json
file before.
class ApplicationController
def supported_browser?
@browsers ||= JSON.parse(Rails.root.join("browsers.json").read)
matcher = BrowserslistUseragent::Match.new(@browsers, request.user_agent)
matcher.browser? && matcher.version?(allow_higher: true)
end
helper_method :supported_browser?
end
Then add this code to you Rails-application layout template:
- if !supported_browser?
.div
div( style: "position: fixed; bottom: 0; right: 0; padding: 8px 10px; background: #e9502f; color: white; width: 100%; z-index: 10; text-align: center;" )
.div
= t('unsupported_browser')
❗This old-fashioned style is deliberately chosen: 'style'-attributes will work mostly everywhere!
Here it is. It will work well for the Rails projects where all the frontend and backend live together as one solid project.
Separated frontend and backend projects
If you have separated projects for Ruby backend and Node.js frontend, you will prefer to get browsers.json over HTTP. You will need to do the following:
- serve the
/browsers.json
path to render the browserslist output by putting it to thepublic
folder:
fs.writeFileSync(
path.join(__dirname, 'public', 'browsers.json'),
JSON.stringify(browserslist(undefined, { path: path.join(__dirname, '..') }))
)
- get in over HTTP in the ruby-backend code:
browsers = JSON.parse(Faraday.get('http://frontend-domain.local/browsers.json').body)
matcher = BrowserslistUseragent::Match.new(browsers, request.user_agent)
matcher.browser? && matcher.version?(allow_higher: true)
Or use the faraday-http-cache to cache the results of the http request. It will force to make one request per the Rails application instance only:
# caches http response locally with etag
http_client = Faraday.new do |builder|
builder.use Faraday::HttpCache, store: Rails.cache
builder.adapter Faraday.default_adapter
end
browsers = JSON.parse(
http_client.get('http://frontend-domain.local/browsers.json').body
)
...
That's it. This solution will use one browserslist.rc
config in the frontend repository, which will automatically be shared over the backend.
More details abort the browserslist_useragent
gem you will find here.
Thanks for reading!
Top comments (4)
FYI "browsers" is mispelled as "browers" in 3 places.
Thanks Andy. Fixed. ☺️
Unsupporting old browsers is very reasonable for the services with saas-service with modern complicated UI.
appreciated, we don't want outdated browsers anywhere.