Skip to content

OS Detect

v2.2.1UtilitiesVanilla JSVueReact

Lightweight OS, form-factor, and runtime detection for browsers, Node.js, and SSR — with React hooks and Vue composables. No dependencies.

OS Detect
Get started →
npm install os-detect@latest
01 — Purpose

When you'd reach for this

navigator.userAgent lies more often than you'd think: iPadOS pretends to be a Mac, and naive parsing catches Windows 10 where it's actually Windows 11 — os-detect takes on these edge cases instead of a hand-rolled regexp.

Keyboard shortcuts should say Cmd, not Ctrl

A "Ctrl+K" hint looks out of place on a Mac, and "⌘K" looks wrong on Windows. The package figures out which operating system it's running on, without parsing the user agent string by hand.

An iPad pretends to be a desktop Mac

Since iPadOS 13, the browser reports itself as "Macintosh," and a naive check would mistake a tablet for a laptop. An extra check for touch support tells them apart correctly.

A feature only exists on one version of an OS

A new operating-system feature only works on Windows 11 — telling it apart from Windows 10 and older needs to work the same way in the browser and on the server.

OS detection shouldn't break server rendering

Code that directly reads browser globals on the server just crashes during server-side rendering — OS detection stays safe for that case and updates itself once the work moves to the browser.

Picking a design language for the platform, not just the OS

Windows should feel like Fluent, Android like Material — but a phone and a desktop browser on the same OS still want different layouts. OS and form factor together answer both questions instead of guessing from screen width alone.

A hybrid device's keyboard gets attached or detached mid-session

A 2-in-1 laptop (or a foldable) can go from touch-only to mouse-primary without a page reload. Checking touch support once at load time misses that entirely — the primary-input check here updates itself live instead.

02 — Features

At a glance

OS detection and boolean checks

OS detection and boolean checks

The getOS() function returns the current OS identifier: iOS, macOS, Android, Windows, Linux, ChromeOS, or unknown. Additional boolean functions (detectIsIOS, detectIsWindows, and others) let you flexibly check specific systems with no extra dependencies.

Async Windows 11 detection

Async Windows 11 detection

detectIsWindows11() asynchronously detects Windows 11 in the browser (via getHighEntropyValues) and in Node.js (via os.release). Returns true only for Windows 11, letting you distinguish it from Windows 10 and older without complex parsing.

Form factor, device categories, and correct iPadOS

Form factor, device categories, and correct iPadOS

isMobileDevice() and isDesktopDevice() provide quick coarse classification, and getFormFactor() goes further — phone, tablet, desktop, or TV, driven by OS and physical screen size rather than touch capability. The iOS detector correctly handles iPadOS 13+, which masquerades as Macintosh in the userAgent, using navigator.maxTouchPoints for accurate differentiation. Separate checks cover whether a touchscreen exists at all, which input type is primary right now, and the device pixel ratio.

Ready-to-use hooks for React and Vue

Ready-to-use hooks for React and Vue

useOS(), useFormFactor(), and useRuntime() (sync), plus useIsWindows11() (async with null state) are available for React and Vue. usePrimaryInput() is the one genuinely reactive hook of the set — it updates itself live if a hybrid device's keyboard or mouse gets attached or detached mid-session. React hooks use useState, Vue composables return readonly refs. All are SSR-safe.

Runtime context detection

Runtime context detection

getRuntime() tells a browser tab apart from a Node.js script or a Web Worker, and standalone checks narrow that further: whether the app is running inside Electron (main or renderer process), and whether it's installed and running as a standalone PWA rather than a regular browser tab.

SSR, Node.js, and zero dependencies

SSR, Node.js, and zero dependencies

In Node.js, the library reads process.platform, and every function caches its result after the first call — except the two whose answer can genuinely change mid-session (primary input type, pixel ratio), which stay live on purpose. Cache reset is available for testing. The package has no external dependencies, ships in ESM, CJS, and UMD, with React and Vue as optional peer dependencies.

03 — Quick example

See how it works

One sync check, everywhere: browser, Node, SSR

getOS() and the device-category checks are cached and behave identically in the browser, in Node, and on the server — no separate server-only code path needed.

basics.ts
import { getOS, isMobileDevice, isDesktopDevice } from 'os-detect'

getOS() // 'windows' | 'macos' | 'ios' | 'android' | 'linux' | 'chromeos' | 'unknown'
isMobileDevice() // true on iOS or Android
isDesktopDevice() // true on macOS, Windows, Linux, or ChromeOS

// All synchronous and cached — and work identically in the browser, in
// Node, and during SSR, no separate server-only code path needed.

Tells Windows 11 from Windows 10, not just "Windows"

Most OS detectors stop at the userAgent string and can't get past plain "Windows" — this one asks the browser's own Client Hints API (or os.release() in Node) to actually know.

windows11.ts
import { detectIsWindows11 } from 'os-detect'

const isWin11 = await detectIsWindows11() // true only on Windows 11

// Most OS detectors stop at the userAgent string, which can't tell Windows
// 10 from 11 — this one asks the browser's own Client Hints API (or
// os.release() in Node) to actually know.

Picking a design language from OS + form factor together

getFormFactor() looks at OS and physical screen size, not touch capability — a touchscreen Windows laptop still comes back 'desktop', not 'tablet'.

formfactor.ts
import { getOS, getFormFactor } from 'os-detect'

if (getOS() === 'windows' && getFormFactor() === 'desktop') {
  loadFluentDesignSystem()
} else if (getOS() === 'android') {
  loadMaterialDesignSystem()
}

// getFormFactor() is driven by OS and physical screen size, not touch
// capability — a touchscreen Windows laptop still comes back 'desktop'.