Detect Browser in JavaScript: Name, Version and Safari Check

Muhaymin Bin Mehmood

Muhaymin Bin Mehmood

·
9 min read
Published Sep 30, 2024
Updated Oct 9, 2026
Learn How to Detect Tab Change in JavaScript Blog Banner Image
13 min read
1,311 words
20 sections11 code blocks

To detect the user's browser in JavaScript, read navigator.userAgent and match it against each browser's token, in the right order. This guide gives you a tested function that returns the browser name and version, shows how to detect Safari correctly, and explains when you should check for features instead.

JavaScript
import { detectBrowser } from "./detect-browser.js";

const browser = detectBrowser();
console.log(browser);
// In Chrome 156 on Windows:
// { name: "Chrome", version: "156.0.8078.4", majorVersion: 156, mobile: false, iOS: false }

Tested on: 78 user-agent strings (the 67 in Playwright's device list plus 11 documented formats for Opera, Samsung Internet, iPhone browsers, Firefox for Android and Internet Explorer), and in real Chromium 156, Chromium 152 and WebKit 27.2 (Safari's engine), October 2026. Firefox was tested through its user-agent strings.

Should You Detect the Browser at All?

Often not. If you want to know whether a feature works, check for the feature itself (see Feature Detection below): that keeps working when browsers add support, and it can't be fooled by a changed user agent. Browser detection is the right tool when you need the name itself:

  • analytics and logging (which browsers your users have);
  • working around a known bug in a specific browser version;
  • showing browser-specific instructions, such as how to install your app on an iPhone or allow notifications;
  • support messages like "please update Safari".

Never use it for security. Users and extensions can change the user agent to anything.

What a User-Agent String Looks Like

Every browser sends a user-agent string, and most of it is there for historical compatibility. Almost all of them start with Mozilla/5.0 and most contain Safari:

Bash
# Chrome on Windows
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36

# Edge on Windows: the same, plus "Edg/"
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36 Edg/156.0.8078.4

# Firefox on Windows
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:157.0) Gecko/20100101 Firefox/157.0

# Safari on macOS
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/27.2 Safari/605.1.15

# Chrome on iPhone: "CriOS", and no "Chrome"
Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/126.0.6478.54 Mobile/15E148 Safari/604.1

So you can't just search for a browser's name. Edge contains Chrome, Chrome contains Safari, and Chrome on iPhone doesn't contain Chrome at all. What works is checking the most specific tokens first.

Detect the Browser Name and Version

JavaScript
// Order matters: Edge, Opera and Samsung Internet also say "Chrome",
// and almost every browser says "Safari".
const BROWSERS = [
  { name: "Edge", pattern: /Edg(?:e|A|iOS)?\/([\d.]+)/ },
  { name: "Opera", pattern: /(?:OPR|OPT)\/([\d.]+)/ },
  { name: "Samsung Internet", pattern: /SamsungBrowser\/([\d.]+)/ },
  { name: "Firefox", pattern: /(?:Firefox|FxiOS)\/([\d.]+)/ },
  { name: "Chrome", pattern: /(?:Chrome|CriOS)\/([\d.]+)/ },
  { name: "Safari", pattern: /Version\/([\d.]+).*Safari\// },
  { name: "Internet Explorer", pattern: /(?:MSIE |Trident\/.*rv:)([\d.]+)/ },
];

export function detectBrowser(userAgent = navigator.userAgent) {
  for (const { name, pattern } of BROWSERS) {
    const match = userAgent.match(pattern);
    if (match) {
      return {
        name,
        version: match[1],
        majorVersion: parseInt(match[1], 10),
        mobile: /Mobi|Android|iPhone|iPad/.test(userAgent),
        // On iPhone and iPad every browser runs on Safari's engine (WebKit).
        iOS: /iPhone|iPad|iPod/.test(userAgent),
      };
    }
  }
  return { name: "Unknown", version: "", majorVersion: NaN, mobile: false, iOS: false };
}
  • Order is everything. Edge (Edg/, EdgA/ on Android, EdgiOS/ on iPhone), Opera (OPR/) and Samsung Internet are checked before Chrome because their strings also contain Chrome/. Safari comes last of the modern browsers because nearly everything says Safari/.
  • iPhone browsers have their own tokens: CriOS for Chrome, FxiOS for Firefox and EdgiOS for Edge.
  • Safari's version is in Version/, not in Safari/ (that number is the WebKit build).
  • One answer: the function returns a single name, plus the version as a string and as a number for comparisons.
  • Browsers it doesn't know return "Unknown". In our test that happened for two very old devices, a 2013 Kindle Fire and a Nokia N9, which is the honest answer.

All 78 user agents in our test came back with the right name and version. Pass any string to test it, for example detectBrowser(someUserAgent), which also makes it usable on the server.

Why Simple includes() Checks Fail

Many tutorials, including an earlier version of this article, use checks like these:

JavaScript
// Common pattern, including an earlier version of this article. Don't use it.
const isChrome = ua.includes("Chrome") && !ua.includes("OPR");
const isFirefox = ua.includes("Firefox");
const isSafari = ua.includes("Safari") && !ua.includes("Chrome");
const isIE = ua.includes("MSIE") || ua.includes("rv:"); // Firefox has "rv:" too
const isOpera = ua.includes("OPR");

We ran this code on the same 78 user agents and it was wrong or ambiguous for 14 of them:

BrowserOld checks returnedWhy
Firefox (Windows, Android)firefox + ieFirefox's user agent contains "rv:"
Microsoft EdgechromeEdge's user agent contains "Chrome"
Samsung InternetchromeIts user agent contains "Chrome"
Chrome on iPhonesafariIt says "CriOS", not "Chrome"
Firefox and Edge on iPhonesafari"FxiOS" / "EdgiOS" weren't checked

How to Detect Safari

"Is this Safari?" usually means one of two things, so decide which you need:

JavaScript
const { name, iOS } = detectBrowser();

const isSafari = name === "Safari";   // Apple's Safari app, on Mac, iPhone or iPad
const usesSafariEngine = isSafari || iOS; // also Chrome, Firefox, Edge on iPhone/iPad
  • isSafari is true only for Apple's Safari. It's the right check for analytics or Safari-specific instructions.
  • On iPhone and iPad, Chrome, Firefox and Edge all use Safari's engine (WebKit). If you're working around a Safari rendering or JavaScript bug, it affects those browsers too, so check usesSafariEngine.
  • iPads in desktop mode send a Mac user agent, so an iPad can show up as iOS: false. If touch matters, also check navigator.maxTouchPoints > 1.

Check the Browser Version

Compare majorVersion, which is a number:

JavaScript
const { name, majorVersion } = detectBrowser();

if (name === "Safari" && majorVersion < 17) {
  showNotice("Please update Safari to use this feature.");
}

Chromium browsers now freeze parts of the user agent. For example, Windows 11 still reports Windows NT 10.0. If you need precise OS or full version details in Chromium, use Client Hints.

User-Agent Client Hints (Chromium Only)

navigator.userAgentData is a structured replacement for the user-agent string. Chrome, Edge, Opera and Samsung Internet support it; Firefox and Safari don't, so use it with a fallback:

JavaScript
// User-Agent Client Hints: Chromium browsers only (Chrome, Edge, Opera, Samsung Internet).
// Firefox and Safari don't have navigator.userAgentData, so always keep a fallback.
export async function getBrowserFromClientHints() {
  const uaData = navigator.userAgentData;
  if (!uaData) return null;

  // Brands include a deliberately fake entry like "Not.A/Brand"; skip it
  // and prefer a specific browser over the generic "Chromium".
  const real = uaData.brands.filter((b) => !/Not.?A.?Brand/i.test(b.brand));
  const brand = real.find((b) => b.brand !== "Chromium") ?? real[0];

  // Low-entropy values are available right away.
  const result = {
    name: brand?.brand ?? "Unknown",
    majorVersion: Number(brand?.version),
    mobile: uaData.mobile,
    platform: uaData.platform,
  };

  // The full version and OS version need an async request.
  const high = await uaData.getHighEntropyValues(["fullVersionList", "platformVersion"]);
  result.version = high.fullVersionList?.find((b) => b.brand === result.name)?.version ?? "";
  result.platformVersion = high.platformVersion;
  return result;
}
JavaScript
const info = (await getBrowserFromClientHints()) ?? detectBrowser();
console.log(info.name, info.version);
  • Brands list the browser and its engine, for example Microsoft Edge and Chromium, plus a deliberately fake brand such as Not?A_Brand that stops sites from depending on exact lists. The function skips the fake brand and prefers the specific browser over "Chromium". Apps built on Chromium that don't add their own brand report just "Chromium"; ours did.
  • High-entropy values (full version, OS version, device model) need getHighEntropyValues(), which returns a promise and only works on HTTPS or localhost.
  • Windows 11: the user agent can't tell it apart from Windows 10, but platformVersion can: 13 or higher means Windows 11, per Microsoft's guide. On our Windows 10 test machine it was 10.0.0.

In WebKit, navigator.userAgentData was undefined, the function returned null, and detectBrowser() gave Safari 27.2. See MDN's Client Hints reference for every field.

Feature Detection: Usually the Better Choice

If the question is "can I use X?", ask the browser directly:

JavaScript
// Check for the feature you need instead of guessing from the browser name.
const supports = {
  share: "share" in navigator,
  clipboard: !!navigator.clipboard?.writeText,
  serviceWorker: "serviceWorker" in navigator,
  viewTransitions: "startViewTransition" in document,
  cssHas: CSS.supports("selector(:has(a))"),
  containerQueries: CSS.supports("container-type: inline-size"),
};

const shareButton = document.querySelector("#share"); // <button id="share" hidden>Share</button>
if (supports.share) {
  shareButton.hidden = false;
  shareButton.addEventListener("click", () =>
    navigator.share({ title: document.title, url: location.href }),
  );
}
Instead ofCheck
"Is it Safari? Then no Web Share""share" in navigator
"Is it old Chrome? Then no :has()"CSS.supports("selector(:has(a))")
"Is it Firefox? Then no View Transitions""startViewTransition" in document
"Is it IE?"Check the exact API you're about to call

In CSS, @supports does the same job without any JavaScript. For handling page visibility instead of browser type, see our guide to detecting tab changes in JavaScript.

Detecting the Browser on the Server

Because detectBrowser() takes the string as a parameter, you can run it on the request's User-Agent header:

JavaScript
// Next.js route handler: the same function works on the User-Agent header
import { detectBrowser } from "@/lib/detect-browser";

export async function GET(request) {
  const browser = detectBrowser(request.headers.get("user-agent") ?? "");
  return Response.json(browser);
}

Server-side detection is useful for logs and analytics. Be careful about sending different HTML to different browsers: if a CDN caches the page, add Vary: User-Agent or one browser's version can be served to everyone.

Brave, Headless Browsers and Other Special Cases

  • Brave sends exactly the same user agent as Chrome on purpose, so detectBrowser() reports Chrome. Brave exposes navigator.brave.isBrave() if you need to know.
  • Headless Chrome (used by Playwright, Puppeteer and many bots) says HeadlessChrome/156…, which the function reports as Chrome. Don't rely on that to spot bots; most change their user agent.
  • Android WebView (in-app browsers) contains ; wv) and Version/4.0 and is reported as Chrome.
  • Internet Explorer is detected (MSIE or Trident/), but Microsoft ended its support in 2022, so you rarely need a special case for it.

Should You Use a Library?

For analytics with many browsers and operating systems, a maintained parser saves time. Bowser (MIT licence, version 2.14 in October 2026) is small and has version ranges built in:

JavaScript
import Bowser from "bowser";

const browser = Bowser.getParser(navigator.userAgent);
browser.getBrowserName();    // e.g. "Microsoft Edge"
browser.getBrowserVersion(); // e.g. "156.0.8078.4"
browser.getOSName();         // e.g. "Windows"
browser.satisfies({ chrome: ">=120", edge: ">=120", safari: ">=17" }); // true or false

The comments show what Bowser 2.14.1 returned for Edge 156 in our test. UAParser.js is another popular option, but check the licence: version 1 is MIT, while version 2 is AGPL-3.0 (or commercial), which matters for closed-source apps.

Common Mistakes

MistakeResultFix
Checking "Chrome" before "Edg" or "OPR"Edge and Opera reported as ChromeCheck the most specific tokens first
Using "rv:" to find Internet ExplorerEvery Firefox is also "IE"Match MSIE or Trident/
Treating every "Safari" as SafariChrome and Edge on iPhone reported as SafariCheck CriOS, FxiOS and EdgiOS first
Reading Safari's version from "Safari/"You get the WebKit build numberRead "Version/"
Using browser checks for featuresBreaks when browsers add supportFeature detection or @supports
Trusting the user agent for securityAnyone can change itCheck permissions on the server

For the full background, MDN's browser detection guide lists the tokens each browser uses.

FAQs

How do I detect the browser in JavaScript?

Read navigator.userAgent and test it with regular expressions in a fixed order: Edge, Opera, Samsung Internet, Firefox, Chrome, then Safari. The detectBrowser() function above returns the name and version.

How do I detect Safari with JavaScript?

Check that the user agent has Version/…Safari/ and none of the other browsers' tokens (detectBrowser().name === "Safari"). A plain includes("Safari") is true for Chrome and Edge as well.

How do I get the browser version in JavaScript?

Use the number after the browser's own token: Chrome/, Firefox/, Edg/, or Version/ for Safari. In Chromium browsers, navigator.userAgentData.getHighEntropyValues(["fullVersionList"]) gives the full version too.

Is navigator.userAgent deprecated?

No, but Chromium browsers freeze parts of it (such as the OS version) for privacy, and Client Hints are the detailed alternative. Firefox and Safari still rely on the user-agent string.

Can I detect Chrome on iPhone?

Yes: its user agent has CriOS/ instead of Chrome/. Remember that it uses Safari's engine, so it has Safari's capabilities and bugs.

How can I tell Brave from Chrome?

Not from the user agent, which is identical. Check navigator.brave, which only Brave provides.

Should I detect the browser or the feature?

Detect the feature whenever you're deciding whether something will work. Detect the browser only when you need its name, such as for analytics, instructions or a version-specific bug fix.

No comments yet. Be the first to comment!

Leave a Comment

2000 characters remaining

Related Articles

View all
Muhaymin Bin Mehmood

About Muhaymin Bin Mehmood

Front-end Developer skilled in the MERN stack, experienced in web and mobile development. Proficient in React.js, Node.js, and Express.js, with a focus on client interactions, sales support, and high-performance applications.