X Xerobit

Browser Detection in JavaScript — Feature Detection vs User Agent Parsing

Detect browsers, OS, and device type in JavaScript using user agent strings, feature detection, and the modern Client Hints API. Includes when to use each approach and common...

Mian Ali Khalid · · 5 min read
Use the tool
User Agent Parser
Parse any User-Agent string into browser, OS, device, and engine. Or detect your own. Built on the maintained ua-parser-js dataset.
Open User Agent Parser →

Browser detection in JavaScript should almost always be feature detection — but sometimes you genuinely need to know which browser you’re in. Here’s how to do both correctly.

Use the User Agent Parser to parse any UA string instantly.

Feature detection (preferred)

Check for capability, not browser identity:

// Bad: browser sniffing
if (navigator.userAgent.includes('Chrome')) {
  doChromeThing();
}

// Good: feature detection
if ('IntersectionObserver' in window) {
  useIntersectionObserver();
} else {
  useFallback();
}

// More examples:
const supportsWebP = () => {
  const canvas = document.createElement('canvas');
  canvas.width = canvas.height = 1;
  return canvas.toDataURL('image/webp').includes('image/webp');
};

const supportsCSS = (property, value) => {
  return CSS.supports(property, value);
};

supportsCSS('display', 'grid');     // true in all modern browsers
supportsCSS('display', 'masonry');  // true only in Firefox (2024)

User agent parsing (when you need it)

function parseBrowser(ua = navigator.userAgent) {
  if (/Edg\//.test(ua)) return { name: 'Edge', engine: 'Blink' };
  if (/OPR\//.test(ua)) return { name: 'Opera', engine: 'Blink' };
  if (/Chrome\//.test(ua)) return { name: 'Chrome', engine: 'Blink' };
  if (/Firefox\//.test(ua)) return { name: 'Firefox', engine: 'Gecko' };
  if (/Safari\//.test(ua) && /Version\//.test(ua)) return { name: 'Safari', engine: 'WebKit' };
  return { name: 'Unknown', engine: 'Unknown' };
}

function parseOS(ua = navigator.userAgent) {
  if (/Windows NT 10.0/.test(ua)) return 'Windows 10/11';
  if (/Windows NT 6.1/.test(ua)) return 'Windows 7';
  if (/Mac OS X/.test(ua)) return 'macOS';
  if (/Android/.test(ua)) return 'Android';
  if (/iPhone|iPad/.test(ua)) return 'iOS';
  if (/Linux/.test(ua)) return 'Linux';
  return 'Unknown';
}

function isMobile(ua = navigator.userAgent) {
  return /Mobi|Android|iPhone|iPad|iPod/i.test(ua);
}

Modern Client Hints API

The User-Agent Client Hints API provides structured data without parsing strings:

// Asynchronous API (requires HTTPS):
async function getClientInfo() {
  if (!navigator.userAgentData) {
    return null; // Not supported (Firefox, Safari)
  }
  
  // Basic info (synchronous):
  const basic = {
    brands: navigator.userAgentData.brands,
    mobile: navigator.userAgentData.mobile,
    platform: navigator.userAgentData.platform,
  };
  
  // Full info (async, requires permission):
  const full = await navigator.userAgentData.getHighEntropyValues([
    'architecture',
    'bitness',
    'model',
    'platformVersion',
    'uaFullVersion',
  ]);
  
  return { ...basic, ...full };
}

// Example result:
// {
//   brands: [{ brand: 'Chromium', version: '120' }, { brand: 'Google Chrome', version: '120' }],
//   mobile: false,
//   platform: 'Windows',
//   platformVersion: '10.0.0',
//   architecture: 'x86',
//   bitness: '64',
// }

Detect device type

function getDeviceType() {
  const ua = navigator.userAgent;
  
  if (/iPad|tablet|Tablet/i.test(ua) && !/Mobile/i.test(ua)) {
    return 'tablet';
  }
  if (/Mobi|Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua)) {
    return 'mobile';
  }
  return 'desktop';
}

// CSS media query approach (more reliable):
function getDeviceTypeCSS() {
  if (window.matchMedia('(max-width: 767px)').matches) return 'mobile';
  if (window.matchMedia('(max-width: 1023px)').matches) return 'tablet';
  return 'desktop';
}

// Or use the pointer media query:
function hasTouchInput() {
  return window.matchMedia('(hover: none) and (pointer: coarse)').matches;
}

Browser version extraction

function getBrowserVersion(ua = navigator.userAgent) {
  const patterns = {
    Edge:    /Edg\/(\d+)/,
    Opera:   /OPR\/(\d+)/,
    Chrome:  /Chrome\/(\d+)/,
    Firefox: /Firefox\/(\d+)/,
    Safari:  /Version\/(\d+)/,
  };
  
  const browser = parseBrowser(ua);
  const pattern = patterns[browser.name];
  if (!pattern) return null;
  
  const match = ua.match(pattern);
  return match ? parseInt(match[1]) : null;
}

// Check minimum version:
function requiresChrome(minVersion) {
  const browser = parseBrowser();
  const version = getBrowserVersion();
  if (browser.name === 'Chrome' && version < minVersion) {
    console.warn(`Chrome ${minVersion}+ required, found ${version}`);
    return false;
  }
  return true;
}

Bot detection

function isBot(ua = navigator.userAgent) {
  const botPatterns = [
    /Googlebot/i,
    /Bingbot/i,
    /Slurp/i,        // Yahoo
    /DuckDuckBot/i,
    /Baiduspider/i,
    /YandexBot/i,
    /Sogou/i,
    /facebookexternalhit/i,
    /LinkedInBot/i,
    /Twitterbot/i,
    /bot|crawler|spider|scraper/i,
  ];
  return botPatterns.some(pattern => pattern.test(ua));
}

// More reliable: check for headless browser signals
function isHeadless() {
  return (
    navigator.webdriver === true ||
    !window.chrome ||
    navigator.languages.length === 0
  );
}

Server-side detection (Node.js)

// With the 'ua-parser-js' library:
import UAParser from 'ua-parser-js';

function parseUA(userAgentString) {
  const parser = new UAParser(userAgentString);
  return {
    browser: parser.getBrowser(),   // { name: 'Chrome', version: '120.0.0.0' }
    os: parser.getOS(),             // { name: 'Windows', version: '10' }
    device: parser.getDevice(),     // { model: '', type: '', vendor: '' }
    engine: parser.getEngine(),     // { name: 'Blink', version: '120.0.0.0' }
  };
}

// In Express middleware:
app.use((req, res, next) => {
  req.clientInfo = parseUA(req.headers['user-agent']);
  next();
});

Related posts

Related tool

User Agent Parser

Parse any User-Agent string into browser, OS, device, and engine. Or detect your own. Built on the maintained ua-parser-js dataset.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.