API Reference
uaBrowser()
Detect the current browser environment and return a full environment info object. Automatically reads navigator.userAgent and injects the navigator context (language, platform, touch points).
import uaBrowser from 'ua-browser'
uaBrowser(): EnvOptionReturns: EnvOption
Example:
const info = uaBrowser()
console.log(info.browser) // 'Chrome'
console.log(info.os) // 'Windows'Notes:
- Returns
'unknown'for fields that cannot be determined, never an empty string. - In Node.js,
navigatoris unavailable;languageandplatformwill be'unknown'. - To parse an arbitrary UA string, use
parseUA(). - For higher accuracy in browsers, use
uaBrowser.detect().
The default export also exposes the following static members:
uaBrowser.detect(): Promise<EnvOption>
uaBrowser.isWebview(ua: string): boolean
uaBrowser.getLanguage(): string
uaBrowser.VERSION: stringuaBrowser.detect()
Async, high-accuracy version of uaBrowser(). Internally calls getEnvContext() to collect hardware and browser signals before parsing, enabling more accurate device type and CPU architecture detection.
This is the recommended entry point for browser-side code.
uaBrowser.detect(): Promise<EnvOption>Returns: Promise<EnvOption>
Signals collected by detect():
| Signal | Purpose |
|---|---|
Client Hints (Sec-CH-UA-*) | Exact version, platform, architecture |
| WebGL renderer / vendor | GPU type → mobile vs. desktop, Apple Silicon vs. Intel |
CSS env(safe-area-inset-top) | iOS notch / Dynamic Island → confirms mobile device |
devicePixelRatio | Phone (≥3) vs. Mac (2) vs. monitor (1–2) |
| Vibration / DeviceMotion APIs | Mobile-only APIs → confirms mobile intent |
Network type (connection.effectiveType) | Supplements device classification |
| Font probes | OS-level font availability |
Example:
import uaBrowser from 'ua-browser'
const result = await uaBrowser.detect()
console.log(result.device) // 'Mobile' — correct even in desktop mode
console.log(result.arch) // 'arm64' or 'x86_64'Notes:
- Browser-only. In Node.js
getEnvContext()returns empty context, so the result is equivalent touaBrowser(). - All DOM accesses are wrapped in
try/catchand will not throw.
parseUA(ua, options?)
Pure function: no global state, no DOM access. Suitable for SSR, Node.js, and unit testing.
import { parseUA } from 'ua-browser'
parseUA(ua: string, options?: ParseOptions): EnvOption| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | UA string to parse. |
options | ParseOptions | No | Inject context; see below. |
ParseOptions fields:
| Field | Type | Description |
|---|---|---|
nav | NavContext | Browser environment subset (language, platform, touch points). Use getNavContext() to read from navigator. |
windowsVersion | string | null | Pre-resolved Windows version string from getWindowsVersion(). Needed to distinguish Windows 10 vs. 11. |
ctx | EnvContext | Full multi-signal context from getEnvContext(). Takes priority over nav and windowsVersion when both are provided. |
customBotDefs | readonly BotDef[] | Custom bot detection rules inserted before the GenericBot catch-all. Does not affect global state. |
language | string | Explicit language override (BCP47). Highest priority — overrides all navigator/header sources. Useful for server-side rendering when passing Accept-Language. |
Returns: EnvOption
Examples:
// Minimal: UA string only
const result = parseUA(navigator.userAgent)
// With navigator context (language/platform populated)
import { parseUA, getNavContext } from 'ua-browser'
const nav = getNavContext()
const result = parseUA(navigator.userAgent, { nav })
console.log(result.language) // 'en-US'
console.log(result.platform) // 'Win32'
// With full env context (multi-signal detection enabled)
import { parseUA, getEnvContext } from 'ua-browser'
const ctx = await getEnvContext()
const result = parseUA(navigator.userAgent, { ctx })
console.log(result.arch) // 'arm64' (from WebGL / Client Hints)
// With custom bot rules
import { parseUA } from 'ua-browser'
import type { BotDef } from 'ua-browser'
const myBots: BotDef[] = [{ name: 'GenericBot', detect: /MyInternalCrawler/ }]
const result = parseUA(ua, { customBotDefs: myBots })parseHeaders(headers)
Parse UA and Client Hints from HTTP request headers. Returns EnvOption. Suitable for precise SSR detection.
import { parseHeaders, ACCEPT_CH } from 'ua-browser'
parseHeaders(headers: Record<string, string | string[] | undefined>): EnvOption| Parameter | Type | Required | Description |
|---|---|---|---|
headers | Record<string, string | string[] | undefined> | Yes | HTTP request headers object (e.g. req.headers in Express / Next.js). |
Returns: EnvOption
Client Hints headers read:
| Header | Data |
|---|---|
user-agent | Full UA string |
sec-ch-ua | Browser brand list |
sec-ch-ua-full-version-list | Exact browser version |
sec-ch-ua-platform | OS name |
sec-ch-ua-platform-version | OS version (distinguishes Windows 10 / 11) |
sec-ch-ua-arch | CPU architecture (e.g. x86, arm) |
sec-ch-ua-mobile | Mobile hint |
Two-request pattern:
On the first request, browsers only send the user-agent header. Return ACCEPT_CH in the response to request Client Hints; they arrive on subsequent requests.
import { parseHeaders, ACCEPT_CH } from 'ua-browser'
// First response — tell the browser to send Client Hints
res.setHeader('Accept-CH', ACCEPT_CH)
// Subsequent requests — full Client Hints included
const result = parseHeaders(req.headers)
console.log(result.arch) // 'x86_64' (from Sec-CH-UA-Arch)
console.log(result.os) // 'Windows'Notes:
- Works with Express, Koa, Next.js API routes, Fastify, Hono, etc. — any framework that exposes headers as a plain object.
- If Client Hints headers are absent, falls back to UA-only parsing.
getEnvContext()
Collect all available browser signals in one async call and return an EnvContext object. Pass the result to parseUA({ ctx }) for multi-signal detection.
import { getEnvContext } from 'ua-browser'
getEnvContext(): Promise<EnvContext>Returns: Promise<EnvContext>
Signals collected:
| Category | Signals |
|---|---|
| Client Hints | platform, platformVersion, architecture, fullVersionList |
| WebGL | GPU renderer + vendor, max texture size, compressed texture formats (ASTC/ETC2/PVRTC/S3TC) |
| Screen | devicePixelRatio, screenWidth, screenHeight |
| CSS env | safe-area-inset-top (iOS notch / Dynamic Island) |
| Hardware APIs | hardwareConcurrency, deviceMemory, vibration API, DeviceMotion event |
| Input | pointerType (coarse/fine/none), hover capability |
| Network | connection.effectiveType, connection.saveData |
| Audio | Sample rate |
| Fonts | OS-specific font availability probes |
Example:
import { getEnvContext, parseUA } from 'ua-browser'
const ctx = await getEnvContext()
const result = parseUA(navigator.userAgent, { ctx })
console.log(result.device) // 'Mobile' — correct even in desktop mode
console.log(result.arch) // 'arm64' (Apple Silicon) or 'x86_64' (Intel)
console.log(result.language) // 'en-US'Notes:
- Browser-only. Safe to call in Node.js — all DOM accesses are guarded and return
undefined; the result is equivalent togetNavContext(). - Each DOM API is wrapped in an individual
try/catch, so a single permission denial does not block the rest. - Prefer
uaBrowser.detect()if you don't need to reuse thectxobject.
getWindowsVersion(nav)
Asynchronously resolve the accurate Windows version to distinguish Windows 10 from Windows 11 (which share the same UA string: Windows NT 10.0).
import { getWindowsVersion, getNavContext, parseUA } from 'ua-browser'
getWindowsVersion(nav: NavContext): Promise<string | null>| Parameter | Type | Required | Description |
|---|---|---|---|
nav | NavContext | Yes | Browser context; pass getNavContext(). |
Returns: Promise<string | null> — the version string (e.g. '11', '10') or null when unavailable.
Example:
const nav = getNavContext()
const windowsVersion = await getWindowsVersion(nav)
const result = parseUA(navigator.userAgent, { nav, windowsVersion })
console.log(result.osVersion) // '11' or '10'Notes:
- Requires
navigator.userAgentData.getHighEntropyValues()(Chrome 90+, Edge 90+). - Returns
nullin Firefox, Safari, and Node.js —osVersionfalls back to UA-derived value. getEnvContext()already calls this internally; usegetWindowsVersion()directly only when you wantNavContext-level context without the fullEnvContextoverhead.
detectBot(ua, customDefs?)
Standalone bot detector. Returns the bot detection result without running the full parseUA() pipeline.
import { detectBot } from 'ua-browser'
import type { BotDef } from 'ua-browser'
detectBot(ua: string, customDefs?: readonly BotDef[]): { isBot: boolean; botName: BotName; botCategory: BotCategory }| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | UA string to test. |
customDefs | readonly BotDef[] | No | Additional bot rules. Inserted after built-in rules, before GenericBot catch-all. |
Returns: { isBot: boolean; botName: BotName; botCategory: BotCategory }
BotDef shape:
interface BotDef {
name: BotName // the bot label returned in botName
detect: RegExp // matched against the UA string
category: BotCategory // bot classification
}Example:
const { isBot, botName, botCategory } = detectBot(ua)
// isBot: true, botName: 'Googlebot', botCategory: 'search-engine'
// Custom rules
const myDefs: BotDef[] = [
{ name: 'GenericBot', detect: /MyInternalCrawler/ }
]
detectBot(ua, myDefs)
// Or pass through parseUA for a full result
parseUA(ua, { customBotDefs: myDefs })Notes:
- Built-in rules cover 30+ bots including AI training crawlers (GPTBot, ClaudeBot, PerplexityBot, CCBot, etc.).
customDefsdo not modify any global state.
detectArch(ua, ctx?)
Standalone CPU architecture detector. Without ctx, falls back to UA-string heuristics only.
import { detectArch } from 'ua-browser'
detectArch(ua: string, ctx?: EnvContext): ArchName| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | UA string. |
ctx | EnvContext | No | Multi-signal context from getEnvContext(). Enables GPU and Client Hints detection. |
Returns: ArchName — 'x86' | 'x86_64' | 'arm' | 'arm64' | 'unknown'
Detection priority chain:
- Client Hints
Sec-CH-UA-Arch(highest accuracy) - WebGL renderer string (ANGLE → x86/x86_64; Apple GPU → arm64; Adreno/Mali → arm64)
navigator.platform(e.g.'Win32'→ x86_64;'iPhone'→ arm64)- UA string patterns (lowest accuracy — affected by UA freezing)
Example:
import { detectArch, getEnvContext } from 'ua-browser'
const ctx = await getEnvContext()
const arch = detectArch(navigator.userAgent, ctx)
// 'arm64' on Apple Silicon, 'x86_64' on Intel MacdetectHeadless(ua)
Detect whether the UA string indicates a headless browser.
import { detectHeadless } from 'ua-browser'
detectHeadless(ua: string): boolean| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | UA string to test. |
Returns: boolean
Detected markers: HeadlessChrome, Headless, PhantomJS, Electron, Playwright, jsdom, Selenium and other common headless identifiers.
Note: Tools that spoof a real UA (e.g. Puppeteer with a custom UA) cannot be detected by string matching alone.
Example:
detectHeadless('Mozilla/5.0 ... HeadlessChrome/124.0.0.0 ...')
// trueisWebview(ua)
Detect whether the UA indicates an embedded WebView (Android Webview or iOS WKWebView).
import { isWebview } from 'ua-browser'
isWebview(ua: string): boolean| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | UA string to test. |
Returns: boolean
Detection logic:
- Android Webview: UA contains
; wv)marker - iOS WKWebView: Safari UA that lacks both
Version/token andSafari/token (WKWebView strips them)
Example:
isWebview('Mozilla/5.0 (Linux; Android 10; K; wv) ...') // true (Android)
isWebview('Mozilla/5.0 (iPhone ...) ... Mobile/15E148') // true (iOS WKWebView)
isWebview('Mozilla/5.0 ... Version/17.4 ... Safari/604.1') // false (real Safari)getNavContext()
Read the current browser's navigator and return a NavContext object. In Node.js, returns a safe empty object so callers do not need environment checks.
import { getNavContext } from 'ua-browser'
getNavContext(): NavContextReturns: NavContext
Example:
const nav = getNavContext()
const result = parseUA(navigator.userAgent, { nav })
console.log(result.language) // 'en-US'
console.log(result.platform) // 'Win32'Notes:
- Prefer
getEnvContext()when you also need arch / device accuracy signals. getNavContext()is synchronous;getEnvContext()is async.
getLanguage(nav)
Extract the normalized browser language from a NavContext. Normalizes the tag to standard BCP 47 form (e.g. 'en-us' → 'en-US', 'ZH_CN' → 'zh-CN').
import { getLanguage, getNavContext } from 'ua-browser'
getLanguage(nav: NavContext): string| Parameter | Type | Required | Description |
|---|---|---|---|
nav | NavContext | Yes | Browser context. |
Returns: string — normalized language tag, e.g. 'en-US', 'zh-CN', or 'unknown'.
Example:
const nav = getNavContext()
console.log(getLanguage(nav)) // 'en-US'ACCEPT_CH
Constant string containing all Client Hints headers that parseHeaders() can consume. Set it as the Accept-CH response header to request these hints from supporting browsers (Chrome / Edge 90+).
import { ACCEPT_CH } from 'ua-browser'
ACCEPT_CH: string
// 'Sec-CH-UA, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Mobile'Example:
res.setHeader('Accept-CH', ACCEPT_CH)
res.setHeader('Vary', 'Sec-CH-UA, Sec-CH-UA-Full-Version-List') // optional but recommendeddetectBrowser(ua)
Standalone browser detector that does not run the full parseUA() pipeline.
import { detectBrowser } from 'ua-browser'
detectBrowser(ua: string): { browser: BrowserName; version: string; browserType: BrowserType }| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | UA string |
Returns: { browser: BrowserName; version: string; browserType: BrowserType }
Example:
const { browser, version, browserType } = detectBrowser(navigator.userAgent)
// browser: 'Chrome', version: '124.0.0.0', browserType: 'browser'detectOS(ua)
Standalone OS detector that does not run the full parseUA() pipeline.
import { detectOS } from 'ua-browser'
detectOS(ua: string): { os: OsName; osVersion: string; osVersionName: string }| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | UA string |
Returns: { os: OsName; osVersion: string; osVersionName: string }
Example:
const { os, osVersion, osVersionName } = detectOS(navigator.userAgent)
// os: 'Windows', osVersion: '10', osVersionName: 'Windows 10'detectEngine(ua)
Standalone rendering engine detector. Does not run the full parseUA() pipeline.
import { detectEngine } from 'ua-browser'
detectEngine(ua: string): { engine: EngineName; engineVersion: string }| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | User agent string |
Returns: { engine: EngineName; engineVersion: string }
Example:
const { engine, engineVersion } = detectEngine(navigator.userAgent)
// engine: 'Blink', engineVersion: '537.36'detectVendorModel(ua)
Standalone device vendor/model extractor.
import { detectVendorModel } from 'ua-browser'
detectVendorModel(ua: string): VendorModelResult| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | User agent string |
Returns: VendorModelResult
Example:
const { vendor, model } = detectVendorModel(ua)
// vendor: 'Samsung', model: 'SM-G991B'detectDevice(ua)
Standalone device type detector. Does not run the full parseUA() pipeline. UA-only — does not use hardware signals.
import { detectDevice } from 'ua-browser'
detectDevice(ua: string): DeviceName| Parameter | Type | Required | Description |
|---|---|---|---|
ua | string | Yes | User agent string |
Returns: DeviceName
Example:
const device = detectDevice(navigator.userAgent)
// device: 'Mobile'satisfies(info, criteria)
Condition-matching helper. TypeScript-aware, cleaner than chaining && expressions.
import { satisfies } from 'ua-browser'
satisfies(info: EnvOption, criteria: Partial<EnvOption>): boolean| Parameter | Type | Required | Description |
|---|---|---|---|
info | EnvOption | Yes | Return value from parseUA() or uaBrowser() |
criteria | Partial<EnvOption> | Yes | Subset of fields to match against |
Returns: boolean
Example:
import uaBrowser, { satisfies } from 'ua-browser'
const info = uaBrowser()
// equivalent to: info.os === 'iOS' && info.device === 'Mobile'
if (satisfies(info, { os: 'iOS', device: 'Mobile' })) {
// ...
}
// match AI crawlers only
if (satisfies(info, { isBot: true, botCategory: 'ai-llm' })) {
// ...
}
// match in-app browsers (WeChat, DingTalk, etc.)
if (satisfies(info, { browserType: 'app' })) {
// ...
}VERSION
The current library version string, matching version in package.json.
import { VERSION } from 'ua-browser'
VERSION: string // e.g. '2.0.0'