You’ve just built a cool new site and now you want to add search. Maybe GPT-powered vector search, maybe just Algoila. You put in a search button and you want the user to be able to open it with the hotkey Cmd + K
. But should the hotkey be Cmd + K
or Ctrl + K
?
If your user is on a Mac, it should be Cmd
(aka “command” aka ⌘
). Otherwise it should be Ctrl
(aka “control” aka ^
). So how can you tell?
One option is navigator.platform
, a string identifying the platform on which the browser is running (e.g. "MacIntel"
, "Win32"
, etc.). While it’s been supported for quite a while, unfortunately it is now marked as deprecated.
“navigator.platform” MDN support grid
Pre-deprecation, MDN recommended avoiding using this feature except in this case, when identifying the platform’s modifier key.
navigator.platform
should almost always be avoided in favor of feature detection. But there is one case where, among the options you could use,navigator.platform
may be the least-bad option: When you need to show users advice about whether the modifier key for keyboard shortcuts is the⌘
command key (found on Apple systems) rather than the⌃
control key (on non-Apple systems)
See their example reproduced below:
let modifierKeyPrefix = "^"; // control key
if (
navigator.platform.indexOf("Mac") === 0 ||
navigator.platform === "iPhone"
) {
modifierKeyPrefix = "⌘"; // command key
}
If navigator.platform
is deprecated, what should we use instead?
Well MDN points you to navigator.userAgentData.platform
. Here navigator.userAgentData
is of type NavigatorUAData
which has the properties platform
(a string identifying the platform running the browser), mobile
(a boolean set to true
if running on a mobile device), and brands
(an array with brand data about the user’s browser).
As a brief aside, MDN notes that those properties are considered “low entropy”. The NavigatorUAData
object also contains a method for obtaining “high entropy” values from the user-agent – called getHighEntropyValues
– where low entropy values are unlikely to be able to be used to identify the user and high entropy values are more likely to be usable in identifying the user.
“navigator.userAgentData.platform” MDN support grid
Unfortunately, navigator.userAgentData.platform
is considered experimental and currently is only supported by Chromium-based browsers (Chrome, Edge, Opera).
So now we have one deprecated API and one experimental API. Are there any other options? Fortunately, yes! The third option is… the User-Agent
, a string that contains information about how a user is accessing the site (browser type and version, operating system, etc.) and is sent as an HTTP header in requests to the server.
The good news is that User-Agent
data is available and well-supported in both the browser (via navigator.userAgent
; see below support table) and on the server side (again, as an HTTP request header). This means, unlike with navigator.platform
and navigator.userAgentData.platform
, the User-Agent
data can be used with a SSR framework like NextJS, rather than needing to wait for the code to run on the client before determining the user’s OS.
“navigator.userAgent” MDN support grid
The bad news is User-Agent
data can be messy and unreliable. According to MDN:
Browser identification based on detecting the user agent string is unreliable and is not recommended, as the user agent string is user configurable.
That said, while this may not be the most reliable method of browser identification, hopefully the operating system information is a bit more reliable.
MDN has a great article with advice on gleaning information from User-Agent
, which you can find here.
Currently there isn’t one consistent way to detect the user’s OS. Until navigator.userAgentData
is fully supported, we may be stuck polyfilling it with the deprecated navigator.platform
or the unreliable User-Agent
string. Fortunately, between those three APIs you should be able to get a good enough answer. And worst case, if you guess wrong it isn’t the end of the world this is just a progressive enhancement that shouldn’t break your site.
Screenshot from the Astro docs
If you really care that much about making sure you don’t show your user the wrong modifier key you can always skip Ctrl+K
/Cmd+K
and use /
instead, like Astro does!