mrkeyoor.com_
Sun 20 Sept 07:02 UTC
npmUtilsupdated 20 Sept 2026

bowser review

Bowser 2.14.1 reads a User-Agent string and returns named browser, operating system, device class, and rendering engine fields. It also checks detected browser versions against rules such as `>=120` or `~20.1`. The current release fixes package contents so files that callers do not need are less likely to enter a bundle. Client Hints can supplement Chromium's reduced User-Agent data, but the parser still reports what the client claims rather than proving which web features work. Our full browser import was 36.4 KB minified and 7.7 KB gzipped.

41.0Mdownloads / wk
Verdict

Bowser 2.14.1 installed in 0.5 seconds and added one package plus 1 MB in our sandbox, with bundled types, zero audit findings, and a 7.7 KB gzipped full browser import. Install it for support reporting or explicit browser-version rules; use capability tests when the actual question is whether a web API works.

We installed it

Lab card: what happened when we installed bowserScreenshot of bowser documentation
Install✓ · 0.5s1 package on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browser7.7 KBgzipped (36.4 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does bowser install cleanly?

Yes. In a fresh container with an empty cache, npm install bowser finished in 0.5s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does bowser add to a browser bundle?

7.7 KB gzipped (36.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does bowser work with both ESM and CommonJS?

Yes. Both import 'bowser' and require('bowser') worked in Node 22 in our run. The package is published as CommonJS.

Does bowser include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

bowser or ua-parser-js: which should you use?

ua-parser-js: Use it when device vendor, model, CPU, and browser parsing belong in the same result. Bowser 2.14.1 installed in 0.5 seconds and added one package plus 1 MB in our sandbox, with bundled types, zero audit findings, and a 7.7 KB gzipped full browser import.

When should you not use bowser?

Your decision concerns a CSS, DOM, media, or network feature; a direct capability test remains correct when a User-Agent string is spoofed or frozen

API stability5/5Bowser 2 keeps its work around `parse()`, `getParser()`, focused getters, category checks, version comparison, and `satisfies()`. The v2.14.1 release notes describe a packaging correction for unnecessary files, with no caller-facing API change. Client Hints enter as an optional second parameter, so existing User-Agent-only calls remain valid. The README still documents CommonJS, namespace TypeScript, and interoperable default imports against the same exported object.
Docs4/5The README shows the complete parsed shape, all three supported import styles, Client Hints input, brand queries, version operators, and precedence between OS, platform, and general browser rules. It also names the separate polyfilled entry and links the generated method reference. Defensive cases take more digging: callers need to account for absent parsed fields and the `undefined` branch from `satisfies()` rather than assuming every input produces a yes or no answer.
Maintenance4/5GitHub records a repository push on 2026-08-01, and the project is not archived. Version 2.14.1 was published on 2026-02-08 with a focused fix for package files leaking into bundles. The repository currently reports 95 open issues and pull requests together, which is meaningful backlog for a signature database but also visible ongoing work. New browser identities still require maintainers to update rules and publish a release before every consumer recognizes them.
Ecosystem5/5The npm API counted 55,997,319 downloads from 2026-08-19 through 2026-08-25, and GitHub reports 5,750 stars. Bowser runs in browsers and Node, carries its own declarations, and declares no direct dependencies, so it fits into many JavaScript stacks without a peer setup. The field also has active narrower and broader choices, including detect-browser and ua-parser-js, which means teams are not locked into Bowser's result model.

Discussed on

  1. hnNintendo's big piracy case is a sad story312 points
  2. hnGary Bowser and gaming's most infamous piracy case111 points
  3. hnU.S. Seeks 5-Year Prison Sentence for Nintendo 'Hacker' Gary Bowser54 points
  4. hnHacker has to pay Nintendo 25-30% of his salary for the rest of his life36 points
  5. hnOptimizing typography of insect labels using free fonts and free software (2012) [pdf]35 points

Use it if

  • Support logs need the same normalized browser, OS, platform, and engine fields in Node and browser code
  • An unsupported-browser notice needs readable per-browser or per-platform version rules through `satisfies()`
  • A Chromium client can supply `navigator.userAgentData` to refine the brand information in its reduced User-Agent string
  • You want browser identification with zero direct dependencies and TypeScript declarations in the package
Skip it if

Setup reality

Our Bowser 2.14.1 install succeeded in 0.5 seconds inside a clean Node 22 sandbox. It left one package and 1 MB on disk. npm audit found zero known vulnerabilities. The package is 292 KB unpacked, declares zero direct and peer dependencies, and bundles its TypeScript declarations. Both require() and ESM import worked even though the published package is CommonJS and has no exports map. A complete esbuild import produced 36.4 KB minified and 7.7 KB gzipped.

No API key or configuration file is involved. The ordinary entry contains ES5 output but no polyfills. Bowser also documents bowser/bundled for an ES5 build with polyfills, which is meant for old targets. TypeScript callers without esModuleInterop should use import * as Bowser from 'bowser'; the default import is documented for projects that enable that compiler option. Release 2.14.1 fixes unwanted package files entering bundles, rather than changing the parser API.

Server code must check the header before calling the parser because bots, health checks, and tests can omit User-Agent. A recognized result may still lack an OS version, device model, or other nested property. Preserve the original header beside parsed telemetry when an unknown result needs investigation. Detection also inherits the limits of its input: clients can lie, and frozen strings cannot describe details that the browser stopped sending.

Client Hints help only when the caller supplies them. Chromium exposes navigator.userAgentData, while Firefox and Safari do not provide that object. Requests for high entropy hint values are asynchronous, so they do not fit directly into Bowser's synchronous second argument. Treat satisfies() as a three-state result in version 2: true matched and passed, false matched and failed, and undefined found no applicable rule. A plain falsy check would reject an unlisted browser.

Patterns

Return every detected category parse-user-agent

import Bowser from 'bowser';

const result = Bowser.parse(navigator.userAgent);
console.log(result.browser);
console.log(result.os);
console.log(result.platform);
console.log(result.engine);

A category or nested value can be absent when version 2.14.1 has no rule for that part of the input. Read optional properties defensively.

Parse once and read several values query-parser

const parser = Bowser.getParser(navigator.userAgent);

const client = {
  browser: parser.getBrowserName(),
  version: parser.getBrowserVersion(),
  os: parser.getOSName(),
  device: parser.getPlatformType(),
};

One parser instance can answer several getters, so repeated fields do not require reparsing the same User-Agent string.

Set browser rules with platform overrides apply-version-policy

const allowed = parser.satisfies({
  macos: { safari: '>=16' },
  mobile: { safari: '>=16' },
  chrome: '>=120',
  firefox: '>=115',
});

OS and platform entries override a general browser entry. Bowser accepts `>`, `>=`, `<`, `<=`, `=`, and `~` comparisons.

Keep unknown browsers out of the failure branch handle-unknown-policy

const allowed = parser.satisfies(rules);

if (allowed === false) {
  showUpgradeNotice();
} else {
  startApplication();
}

`undefined` means no browser rule matched. Checking `!allowed` would treat that unknown state as a confirmed version failure.

Supplement the User-Agent with Client Hints supply-client-hints

const parser = Bowser.getParser(
  navigator.userAgent,
  navigator.userAgentData
);

console.log(parser.getHints());
console.log(parser.getBrandVersion('Google Chrome'));

Firefox and Safari do not expose `navigator.userAgentData`; Bowser falls back to parsing the first argument when hints are missing.

Parse an HTTP header only when it is a string guard-request-header

function identifyRequest(req) {
  const value = req.headers['user-agent'];
  return typeof value === 'string' ? Bowser.parse(value) : null;
}

Automated clients can omit `User-Agent`. `getParser()` expects a string, so middleware should decide how to represent a missing header.

Limit a workaround to one browser check-browser-family

const parser = Bowser.getParser(navigator.userAgent);

if (parser.isBrowser('Safari')) {
  enableSafariWorkaround();
}

Browser identity is self-reported and can be spoofed. Keep the workaround narrow and prefer testing the affected capability when possible.

Test the version of the detected browser compare-current-version

if (
  parser.isBrowser('Safari') &&
  parser.compareVersion('<16')
) {
  loadCompatibilityCode();
}

`compareVersion()` uses whichever browser Bowser detected. Pairing it with `isBrowser()` prevents a Safari threshold from applying to another family.

Branch on a recognized bot signature classify-declared-bot

const result = Bowser.parse(userAgent);

if (result.platform?.type === 'bot') {
  omitAnalyticsEvent();
}

This detects declared crawler strings present in Bowser's rules. It does not expose a bot that sends an ordinary browser identity.

Check a brand from supplied hints inspect-hint-brand

const parser = Bowser.getParser(userAgent, hints);

if (parser.hasBrand('Google Chrome')) {
  console.log(parser.getBrandVersion('Google Chrome'));
}

The answer comes from the supplied `brands` array and may be null when the requested brand is not present.

Use the CommonJS entry in Node load-commonjs

const Bowser = require('bowser');
const result = Bowser.parse(userAgent);

Bowser 2.14.1 is published as CommonJS without an exports map. Our Node 22 sandbox loaded this form successfully.

Choose the TypeScript import form import-typescript

import * as Bowser from 'bowser';

// With esModuleInterop enabled:
// import Bowser from 'bowser';

The package includes declarations. Its README ties the default import form to TypeScript projects with `esModuleInterop` enabled.

Alternatives

PackageRegistryPick it when
ua-parser-jsnpmUse it when device vendor, model, CPU, and browser parsing belong in the same result.
detect-browsernpmUse it when an application only needs a compact browser name and version check.
platformnpmUse it for a human-readable platform description alongside browser and OS fields.
useragentnpmUse it to maintain an older Node service already coupled to its parser objects and update process.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.