User-Agent Client Hints and navigator.userAgentData

The User-Agent Client Hints (UA-CH) API is a modern web standard designed to replace the legacy, monolithic User-Agent string with a structured, privacy-preserving mechanism for accessing browser and device information. This article explains the fundamentals of User-Agent Client Hints, explores how the JavaScript interface navigator.userAgentData operates, and provides practical code examples demonstrating how to access both basic and high-entropy client data.


What is the User-Agent Client Hints API?

Historically, browsers transmitted a single User-Agent (UA) string via HTTP request headers and exposed it in JavaScript through navigator.userAgent. Over time, this string became bloated, difficult to parse with regular expressions, and a significant vector for passive user fingerprinting and tracking.

The User-Agent Client Hints API solves this by dividing client information into two tiers:

  1. Low-Entropy Hints: Basic information that does not easily identify an individual user. This data is available by default with zero performance or privacy overhead.
  2. High-Entropy Hints: Detailed device and system information (such as exact OS versions, device models, or full browser build numbers) that could aid in fingerprinting. This data is only accessible asynchronously when explicitly requested by the site and permitted by user preferences.

How navigator.userAgentData Works

In supported browsers, the navigator.userAgentData object provides direct access to client hint information in JavaScript. It exposes immediate properties for low-entropy hints and a promise-based method to query high-entropy hints.

1. Reading Low-Entropy Data

Low-entropy data is synchronously available on the navigator.userAgentData object without requiring any network permissions or asynchronous calls.

The core properties include: * brands: An array of objects containing browser brand names and their major version numbers. Browsers often include a “grease” brand to prevent servers from hardcoding strict browser checks. * mobile: A boolean indicating whether the user agent is running on a mobile device. * platform: A string identifying the operating system platform (e.g., "Windows", "macOS", "Android", "Linux").

if ('userAgentData' in navigator) {
  const { brands, mobile, platform } = navigator.userAgentData;

  console.log('Brands:', brands);
  // Example output: [{brand: "Chromium", version: "120"}, {brand: "Google Chrome", version: "120"}]

  console.log('Is Mobile:', mobile); 
  // Example output: false

  console.log('Platform:', platform); 
  // Example output: "macOS"
}

2. Requesting High-Entropy Data

To retrieve specific device details, you must call the getHighEntropyValues() method. This method accepts an array of hint names and returns a Promise that resolves with an object containing the requested values.

Supported high-entropy hints include: * architecture: The CPU architecture (e.g., "x86", "arm"). * bitness: The CPU bitness (e.g., "64", "32"). * formFactor: The device type (e.g., "Desktop", "Phone", "Tablet"). * model: The specific device model (primarily on mobile devices). * platformVersion: The detailed version of the operating system. * fullVersionList: The full version numbers for all identified browser brands. * wow64: A boolean indicating if a 32-bit application is running on 64-bit Windows.

if ('userAgentData' in navigator) {
  navigator.userAgentData.getHighEntropyValues([
    'architecture',
    'model',
    'platformVersion',
    'fullVersionList'
  ])
  .then(hints => {
    console.log('High-Entropy Hints:', hints);
    /*
    Example output:
    {
      architecture: "arm",
      brands: [...],
      fullVersionList: [{brand: "Google Chrome", version: "120.0.6099.109"}, ...],
      mobile: false,
      model: "",
      platform: "macOS",
      platformVersion: "14.2.1"
    }
    */
  })
  .catch(error => {
    console.error('Error fetching client hints:', error);
  });
}

Implementing Fallbacks for Unsupported Browsers

While modern Chromium-based browsers (Chrome, Edge, Opera, Samsung Internet) support the User-Agent Client Hints API, other browsers like Firefox and Safari have chosen alternative approaches to mitigate fingerprinting (such as freezing the traditional User-Agent string) and do not implement navigator.userAgentData.

To maintain compatibility across all environments, use feature detection and fall back to navigator.userAgent:

function getClientPlatform() {
  if ('userAgentData' in navigator && navigator.userAgentData.platform) {
    return navigator.userAgentData.platform;
  }
  
  // Fallback for Safari, Firefox, and legacy browsers
  const ua = navigator.userAgent;
  if (/android/i.test(ua)) return 'Android';
  if (/iPad|iPhone|iPod/.test(ua)) return 'iOS';
  if (/Windows/i.test(ua)) return 'Windows';
  if (/Mac/i.test(ua)) return 'macOS';
  if (/Linux/i.test(ua)) return 'Linux';
  
  return 'Unknown';
}

Summary of Key Differences

Feature Legacy navigator.userAgent Modern navigator.userAgentData
Data Format Unstructured plain text string Structured JavaScript objects and arrays
Access Model All data exposed immediately Tiered (low-entropy sync, high-entropy async)
Privacy Impact High passive fingerprinting risk Controlled exposure via explicit requests
Parsing Requires brittle regex patterns Native property access (.brands, .platform)