March 27, 2020
Translated from the Korean original.
![]()
There’s no avoiding the navigator object in frontend work, so here’s a rundown of what its properties actually do.
It holds more than just the well-known User Agent string — plenty of other information about the user’s state lives on it too, and every one of its properties is read-only.
ChromeLabs built this set of hooks to surface information about the user’s device and network conditions, and under the hood, it’s built entirely on properties from the navigator object.
That includes the properties react-adaptive-hooks itself relies on, so between those and a few more, here’s what actually counts as a “navigator property worth knowing.”
Some of these properties have pretty spotty browser support, so I’ve marked each one with a tier based on how widely it’s supported.
This tells you about the network the user is currently on, and gives you access to the following.
navigator.connection---[Result]NetworkInformation: { onchange: null effectiveType: "4g" rtt: 100 downlink: 10 saveData: false}// Browser Supportconst connection = navigator.connection|| navigator.mozConnection|| navigator.webkitConnection;
function updateConnectionStatus() { alert("Connection bandwidth: " + connection.effectiveType + " MB/s");}
connection.addEventListener("change", updateConnectionStatus);updateConnectionStatus();
Here’s a before-and-after: starting on a 4g connection, then switching to Fast 3G in Chrome’s Network tab. You can watch the effectiveType and downlink values actually change.

This one is still experimental, so support is limited.

Gives you the user’s location, but only once they’ve granted location access in their device settings.
navigator.geolocation.getCurrentPosition(function(position) { console.log(position);}, err => console.log(err));
---[Result]coords: { latitude: longitude longitude: latitude altitude: altitude accuracy: latitude/longitude accuracy altitudeAccuracy: accuracy of the altitude heading: A number representing the direction of travel. The angle deviated clockwise from true north (true north: 0, east: 90) speed: speed}
timestamp: 1500000000function success(pos) { console.log(pos.coords.latitude, pos.coords.longitude)}
function error(err) { console.warn('ERROR(' + err.code + '): ' + err.message);}
navigator.geolocation.watchPosition(success, error);Info about the device’s battery. It returns a Promise, used like this.
navigator.getBattery().then(res => console.log(res))
---[Result]charging: truechargingTime: 0dischargingTime: Infinitylevel: 1onchargingchange: nullonchargingtimechange: nullondischargingtimechange: nullonlevelchange: nullnavigator.getBattery().then(battery => { battery.addEventListener('chargingchagne', () => { console.log('Battery Charging' + battery.charging ? 'yes' : 'no') })})The callback fires every time the charging state changes.
The getBattery API is DEPRECATED and may not work on the latest browsers at all — it got phased out over privacy concerns.

Tells you whether cookies are enabled. If the user has blocked cookies in their browser settings, this comes back false.
Returns whatever language is set on the device.

In Chrome, that’s whatever sits at the top of “Settings > Languages.” Set it to “Korean” and navigator.language comes back ko; set it to “English (United States)” and you get en-US.
Tells you whether the device can encode or decode a given media format.
Only works if the user has turned the feature on. In Chrome, that’s a toggle in settings.
//Create media configuration to be testedconst mediaConfig = { type : 'record', // or 'transmission' video : { contentType : "video/webm;codecs=vp8.0", // valid content type width : 1920, // width of the video height : 1080, // height of the video bitrate : 120000, // number of bits used to encode 1s of video framerate : 48 // number of frames making up that 1s. }};
// check support and performancenavigator.mediaCapabilities.encodingInfo(mediaConfig).then(result => { console.log('This configuration is ' + (result.supported ? '' : 'not ') + 'supported, ' + (result.smooth ? '' : 'not ') + 'smooth, and ' + (result.powerEfficient ? '' : 'not ') + 'power efficient.')});With the feature enabled, running the code above logs “This configuration is supported, not smooth, and not power efficient.”
Without it enabled, you get an
Uncaught TypeErrorinstead.
navigator.mediaCapabilities.decodingInfo({ type : 'file', audio : { contentType : "audio/mp3", channels : 2, bitrate : 132700, samplerate : 5200 }}).then(function(result) { console.log('This configuration is ' + (result.supported ? '' : 'not ') + 'supported, ' + (result.smooth ? '' : 'not ') + 'smooth, and ' + (result.powerEfficient ? '' : 'not ') + 'power efficient.')});decodingInfo doesn’t need any flag turned on. On Chrome 80, running the code above gives “This configuration is supported, smooth, and power efficient.”
Still experimental, so only a handful of browsers support it.

Returns how many points the device can register as simultaneous touches. It goes by TouchEvent, so on a PC, Chrome returns 0 in desktop mode and 1 in mobile mode.
You can try this yourself on CodePen.
// PC - Desktop modenavigator.maxTouchPoints // result: 0
// PC - Mobile modenavigator.maxTouchPoints // result: 1
// Mobile Device - iPhoneXnavigator.maxTouchPoints // result: 5Can I use lists some limitations on Safari, but in my testing it works just fine there.

Returns whether the device is currently online.
// Internet connectednavigator.onLine //true
// Internet disconnectednavigator.onLine // falseWorks in most browsers, with some limitations on IE8.

Lets you check the permission status for anything that needs the user’s OK — push notifications, location, and the like.
navigator.permissions.query({name: 'geolocation'}) .then(res => { if (res.state === 'granted') { console.log('Got permission!') } else if (res.state === 'prompt') { console.log('Permission has never been requested.'); } })There are three possible states. (docs)
query takes a PermissionDescriptor argument, made up of three fields.

Returns what platform it’s running on. On a MacBook Pro that’s MacIntel; here are the common values.
A list of whatever plugins the browser supports. Here’s what it looks like in Chrome.
navigator.mimeTypes
---[Result]0: { 0: MimeType, application/x-google-chrome-pdf: MimeType, name: "Chrome PDF Plugin", filename: "internal-pdf-viewer", description: "Portable Document Format", length: 1}1: { 0: MimeType, application/pdf: MimeType, name: "Chrome PDF Viewer", filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai", description: "", length: 1}2: { 0: MimeType, 1: MimeType, ...You can check whether a specific plugin is installed with the namedItem method.
function getFlashVersion() { var flash = navigator.plugins.namedItem('Shockwave Flash'); if (typeof flash != 'object') { // flash is not present return undefined; } if(flash.version){ return flash.version; } else { //No version property (e.g. in Chrome) return flash.description.replace(/Shockwave Flash /,""); }}Returns a StorageManager object, which supports three methods.
// https://twitter.comnavigator.storage.estimate().then( res => console.log(res))
---[Result]quota: 150411345100usage: 18873830usageDetails: { caches: 18312960, indexedDB: 413132, serviceWorkerRegistrations: 147738}Holds the browser’s name, version, and platform. Every request sent to a server carries a User-Agent (UA from here on) HTTP header — the so-called userAgent string — packed with details like browser type, version number, and host OS.
Here’s what navigator.userAgent actually looks like across a few environments.
Back when Netscape Navigator and IE were the only browsers around, Netscape expressed its version as 'Mozilla/version'. Other browsers later tacked Mozilla/version onto their own userAgent strings just to signal compatibility with a given Netscape version (even though they weren't actually built on it). That's the whole reason so many browsers still start their userAgent with 'Mozilla/version' today.
Starting with Chrome 81, UA is being phased out step by step — advertisers using it to track site visitors, and browser support built on parsing this string, both turned out to cause plenty of problems.
Going forward, Chrome won’t reveal things like whether a user is on Windows 7 or on a specific device. Here’s the rollout plan for phasing UA out.
“On top of those privacy issues, User-Agent sniffing is an abundant source of compatibility issues, in particular for minority browsers, resulting in browsers lying about themselves (generally or to specific sites), and sites (including Google properties) being broken in some browsers for no good reason,” - Yoav Weiss, Google Engineer-
UA is being replaced by a new spec called Client Hints. You opt in either with an Accept-CH request header, or a meta tag.
<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width, Downlink">You request information through either the header or the meta tag, comma-separated. Here’s everything the browser can hand back.
Example
By default, the browser sends back this much.
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.1.2222.33 Safari/537.36Sec-CH-UA: "Chrome"; v="74"Sec-CH-Mobile: ?0You can ask for more, like this
Accept-CH: UA-Full-Version, UA-Platform, UA-Archand get back values like these.
Sec-CH-UA: "Chrome"; v="74"Sec-CH-UA-Full-Version: "74.0.3424.124"Sec-CH-UA-Platform: "macOS"Sec-CH-UA-Arch: "ARM64"Going through all of navigator’s properties, I realized the web can do a lot more than I gave it credit for. Even a rough sense of what’s built into the browser like this seems like it’ll come in handy more often than you’d expect.