Tiny, typed, SSR-safe browser detection.
One call. One canonical answer. ~1.4Β kB min+gzip. Zero dependencies.
π Docs site Β·
π§ͺ Playground Β·
π³ Recipes Β·
import { detect, getOS, isMobile, browsers, oses } from 'get-browser';
if (detect() === browsers.SAFARI && isMobile()) {
applyMobileSafariFix();
}
const shortcut = getOS() === oses.MACOS ? 'β K' : 'Ctrl K';That's the whole pitch. detect() returns a strict browser union β 'chrome' | 'edge' | 'firefox' | 'safari' | 'opera' | 'ie' | 'android' | 'unknown'. getOS() returns a strict OS union β 'macos' | 'windows' | 'linux' | 'ios' | 'android' | 'chromeos' | 'unknown'. A handful of tree-shakeable predicates do the boolean versions.
pnpm add get-browser # or npm / yarn / bunNo bundler? Drop in the UMD bundle:
<script src="https://unpkg.com/get-browser/dist/umd/get-browser.global.js"></script>
<script>
if (GetBrowser.isMobile()) document.body.classList.add('is-mobile');
</script>- πͺΆ Tiny β ~1.4 kB min+gzip, zero dependencies, tree-shakeable.
- π§ Typed β
detect()returns theBrowserunion, neverstring. Exhaustive switches compile. - ποΈ SSR-safe β every detector takes
{ userAgent }. Works in Node, Next.js, Remix, Astro, Workers, Deno. - π― Honest β it answers who, not what. For capability checks use
@supports/matchMedia.
Tip
Want to see it in action without installing anything? Open the Playground β paste any user-agent and watch every predicate light up.
Switch on the browser
import { detect, browsers } from 'get-browser';
switch (detect()) {
case browsers.CHROME: loadChromeShim(); break;
case browsers.SAFARI: patchSafariScrollBug(); break;
case browsers.FIREFOX: enableFirefoxOnlyFeature(); break;
case browsers.UNKNOWN: /* bot or new browser */ break;
}Booleans β tree-shakes to ~400 bytes per predicate
import { isMobile, isChrome, isSafari } from 'get-browser';
if (isMobile() && !isChrome()) showNonChromeMobileBanner();
if (isSafari() && isMobile()) applyMobileSafariFix();Server-side β Next.js, Remix, Workers, Deno
// Next.js Edge route β runs on Cloudflare too
export const runtime = 'edge';
import { detect, getOS } from 'get-browser';
export function GET(req: Request) {
const userAgent = req.headers.get('user-agent') ?? '';
return Response.json({
browser: detect({ userAgent }),
// Prefer Sec-CH-UA-Platform β Chrome's UA Reduction is hollowing
// out the legacy UA string. Get-browser reads either.
os: getOS({
userAgent,
clientHints: { platform: req.headers.get('sec-ch-ua-platform') ?? undefined },
}),
});
}The library never touches window at import time. Pass an explicit UA and detection becomes a pure function β perfect for tests and SSR. Full framework cookbook in the SSR guide.
Cross-platform UI β shortcuts, downloads, deep links
import { getOS, oses } from 'get-browser';
const os = getOS();
const shortcut = os === oses.MACOS ? 'β K' : 'Ctrl K';
const downloadUrl = os === oses.WINDOWS ? '/dl/app.exe'
: os === oses.MACOS ? '/dl/app.dmg'
: os === oses.LINUX ? '/dl/app.deb'
: '/dl/';
const storeUrl = os === oses.IOS ? 'https://apps.apple.com/β¦'
: os === oses.ANDROID ? 'https://play.google.com/β¦'
: '/install';In-app browsers β Instagram, Facebook, TikTok, X, LinkedIn, β¦
import { isInAppBrowser } from 'get-browser';
// OAuth providers (Google, Apple, Microsoft) block sign-in inside
// most in-app browsers. Bounce to the system browser first.
if (isInAppBrowser()) {
showOpenInBrowserBanner({
message: 'Tap β― β "Open in browser" to continue with Google sign-in.',
});
}Catches Facebook (FBAN/FBAV/FB_IAB), Instagram, X/Twitter, LinkedIn, TikTok, Snapchat, WeChat, Line, Telegram, Pinterest. Stable token-based matching β version-agnostic.
Type-safe analytics tagging
import { type Browser, detect } from 'get-browser';
const engineOf = (b: Browser) =>
({
chrome: 'chromium', edge: 'chromium', opera: 'chromium',
firefox: 'gecko', safari: 'webkit', ie: 'trident',
android: 'legacy-webkit', unknown: 'unknown',
} as const)[b];
analytics.track('page_view', { engine: engineOf(detect()) });If a future major bumps Browser, the compiler refuses to build. No silent drift.
A small surface β every export pulls its weight.
detect(opts?) |
Returns one of the browsers values |
getOS(opts?) |
Returns one of the oses values |
isChrome / isEdge / isFirefox / isSafari |
(opts?) => boolean |
isOpera / isIE / isAndroid / isMobile |
(opts?) => boolean |
isInAppBrowser |
(opts?) => boolean β true inside Instagram, Facebook, TikTok, X, LinkedIn, β¦ |
browsers, oses |
Frozen enums: { CHROME: 'chrome', ... }, { MACOS: 'macos', ... } |
Browser, OS, DetectOptions, ClientHints |
Type-only exports |
opts is { userAgent?: string; vendor?: string; clientHints?: { platform?: string } } β pass userAgent for SSR or tests, pass clientHints.platform (the Sec-CH-UA-Platform header) for the most reliable OS read.
| Bundle (min+gz) | get-browser | detect-browser | bowser | ua-parser-js |
|---|---|---|---|---|
| π ~1.4 kB | ~2 kB | ~7 kB | ~10 kB |
Pick ua-parser-js if you need version numbers or device info. Pick get-browser if you just need the single, lowercase, typed answer to which browser is this? β see the full comparison.
Chrome, Edge (legacy & Chromium), Firefox, Safari (desktop, iOS, iPadOS), Opera (Presto & OPR), Internet Explorer 6-11, Android WebView β including iOS variants (CriOS, FxiOS, EdgiOS) and mobile / tablet user-agents. Coverage details: browser support.
- Node β₯ 20 (active LTS β 20, 22, 24)
- TypeScript β₯ 5.0 if you use types
- Browsers β evergreen. UMD bundle is ES2018.
The full docs are built with Docusaurus and deployed to GitHub Pages:
| π Docs | π API | π§ͺ Playground | π³ Recipes | ποΈ SSR | π Migration |
|---|
Run the docs locally:
pnpm install
pnpm run build # build the library first
pnpm run website:install
pnpm run website:dev # http://localhost:3000PRs welcome β see CONTRIBUTING.md. For security issues, see SECURITY.md (please don't open public issues for vulnerabilities).
MIT Β© yankouskia and contributors.