os-detect — определяем операционную систему в браузере, Node.js и SSR

os-detect — определяем операционную систему в браузере, Node.js и SSR
В проектах периодически встречаются задачи, которые требуют специфичного поведения под конкретную платформу: другой UI для мобильных, отдельные инструкции для macOS и Windows, баннеры только для iOS. Чтобы не решать это каждый раз заново, я собрал единую библиотеку.
os-detect — пакет для определения операционной системы и типа устройства. Работает в браузере, в Node.js, в SSR. Есть хуки для React и composables для Vue. Зависимостей — ноль.
Установка
bash
npm install os-detect
Зачем вообще это нужно
Ситуации, когда нужно знать ОС, встречаются чаще, чем кажется:
- Показать инструкцию по установке под нужную платформу
- Скрыть кнопку «Скачать для iOS» на Android
- Вывести разные горячие клавиши для Mac и Windows
- Показать баннер «Доступно в App Store» только на iPhone и iPad
- Отключить какую-то анимацию на мобильных устройствах
Можно каждый раз парсить navigator.userAgent вручную. Или поставить пакет и не думать об этом.
Пример на чистом JS — страница загрузки с разными ссылками под каждую платформу:
js
import { getOS, isMobileDevice } from 'os-detect';
const os = getOS();
const downloadLinks = {
windows: { label: 'Скачать для Windows', url: '/download/app.exe' },
macos: { label: 'Скачать для macOS', url: '/download/app.dmg' },
linux: { label: 'Скачать для Linux', url: '/download/app.AppImage' },
ios: { label: 'App Store', url: 'https://apps.apple.com/...' },
android: { label: 'Google Play', url: 'https://play.google.com/...' },
};
const btn = document.getElementById('download-btn');
if (downloadLinks[os]) {
btn.textContent = downloadLinks[os].label;
btn.href = downloadLinks[os].url;
} else {
// Неизвестная платформа — показываем все варианты
btn.textContent = 'Выбрать версию';
btn.href = '/download';
}
// Скрыть десктопный хедер на мобильных
if (isMobileDevice()) {
document.getElementById('desktop-nav').style.display = 'none';
}
Пример на Vue — блок с горячими клавишами, зависящими от ОС:
vue
<script setup lang="ts">
import { computed } from 'vue';
import { getOS } from 'os-detect';
const os = getOS();
const shortcuts = computed(() => {
const isMac = os === 'macos';
return {
save: isMac ? '⌘ S' : 'Ctrl + S',
undo: isMac ? '⌘ Z' : 'Ctrl + Z',
find: isMac ? '⌘ F' : 'Ctrl + F',
newTab: isMac ? '⌘ T' : 'Ctrl + T',
};
});
</script>
<template>
<div class="shortcuts">
<h3>Горячие клавиши</h3>
<ul>
<li>Сохранить — <kbd>{{ shortcuts.save }}</kbd></li>
<li>Отменить — <kbd>{{ shortcuts.undo }}</kbd></li>
<li>Поиск — <kbd>{{ shortcuts.find }}</kbd></li>
<li>Новая вкладка — <kbd>{{ shortcuts.newTab }}</kbd></li>
</ul>
</div>
</template>
Базовое использование
Самая простая точка входа — функция getOS(). Возвращает строку с названием ОС:
ts
import { getOS } from 'os-detect';
getOS(); // 'ios' | 'macos' | 'android' | 'windows' | 'linux' | 'chromeos' | 'unknown'
Если нужен конкретный boolean — есть отдельная функция для каждой ОС:
ts
import {
detectIsIOS,
detectIsMacOS,
detectIsAndroid,
detectIsWindows,
detectIsLinux,
detectIsChromeOS,
} from 'os-detect';
detectIsIOS(); // true на iPhone, iPod и iPad (включая iPadOS 13+)
detectIsMacOS(); // true на macOS-десктопе
detectIsAndroid(); // true на Android-телефонах и планшетах
detectIsWindows(); // true на Windows
detectIsLinux(); // true на Linux (Android и ChromeOS исключены)
detectIsChromeOS(); // true на ChromeOS
И две вспомогательные функции для тех, кому не важна конкретная ОС:
ts
import { isMobileDevice, isDesktopDevice } from 'os-detect';
isMobileDevice(); // true если iOS или Android
isDesktopDevice(); // true если macOS, Windows, Linux или ChromeOS
Результат каждого вызова кэшируется — повторные обращения бесплатны.
Тонкость с iPadOS 13+
С выходом iPadOS 13 Apple решила, что iPad должен представляться как Mac. Safari на iPad стал отправлять Macintosh в строке User-Agent — такой же, как у настоящего Mac.
Если определять ОС только по UA, detectIsMacOS() вернёт true на iPadе. Это неверно.
В os-detect эта ситуация обрабатывается через navigator.maxTouchPoints:
ts
// iPad с iPadOS 13+ отправляет:
// UA: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) ..."
// maxTouchPoints: 5
detectIsIOS(); // true — правильно
detectIsMacOS(); // false — правильно
Проверка maxTouchPoints > 1 надёжно отличает iPadOS от настоящего Mac.
Современный API: navigator.userAgentData
Последние версии Chrome и Edge поддерживают navigator.userAgentData — стандартный браузерный API, который возвращает платформу напрямую, без парсинга строки UA.
os-detect использует его в первую очередь и падает обратно на userAgent для Safari, Firefox и других браузеров:
navigator.userAgentData.platform → 'Windows' | 'macOS' | 'Android' | 'Linux' | 'iOS'
Это делает определение более точным там, где API доступен.
Windows 11
Отличить Windows 11 от Windows 10 нельзя через обычный userAgent — там всё равно написано Windows NT 10.0. Для этого нужен асинхронный вызов getHighEntropyValues():
ts
import { detectIsWindows11 } from 'os-detect';
const isWin11 = await detectIsWindows11(); // true | false
Windows 11 сообщает platformVersion >= 13.0.0. Если API недоступен или вызов упал — функция тихо возвращает false.
CommonJS и UMD
Для тех, кто не использует бандлер — пакет доступен через CDN:
html
<script src="https://unpkg.com/os-detect/dist/index.umd.js"></script>
<script>
if (OsDetect.detectIsIOS()) {
document.querySelector('.app-store-btn').style.display = 'block';
}
</script>
CommonJS:
js
const { getOS, isMobileDevice } = require('os-detect');
Vue
Для Vue есть готовые composables — импортируются из os-detect/vue:
vue
<script setup lang="ts">
import { useOS, useIsWindows11 } from 'os-detect/vue';
const os = useOS(); // Readonly<Ref<OS>>
const isWin11 = useIsWindows11(); // Readonly<Ref<boolean | null>>
</script>
<template>
<div>
<p>Ваша система: {{ os }}</p>
<div v-if="os === 'ios'">
<a href="https://apps.apple.com/...">Скачать в App Store</a>
</div>
<div v-else-if="os === 'android'">
<a href="https://play.google.com/...">Скачать в Google Play</a>
</div>
<div v-if="os === 'windows'">
<p v-if="isWin11 === null">Определяем версию Windows...</p>
<p v-else-if="isWin11">Вы на Windows 11</p>
<p v-else>Вы на Windows 10 или старше</p>
</div>
</div>
</template>
useOS() — синхронный, результат закэширован. useIsWindows11() возвращает null пока идёт асинхронная проверка, затем true или false.
React
Для React — хуки из os-detect/react:
tsx
import { useOS, useIsWindows11 } from 'os-detect/react';
function DownloadButton() {
const os = useOS();
const isWin11 = useIsWindows11();
if (os === 'ios') {
return <a href="https://apps.apple.com/...">App Store</a>;
}
if (os === 'android') {
return <a href="https://play.google.com/...">Google Play</a>;
}
if (os === 'windows') {
return (
<div>
<a href="/download/windows">Скачать для Windows</a>
{isWin11 === true && <span> (оптимизировано для Windows 11)</span>}
</div>
);
}
return <a href="/download">Скачать</a>;
}
Node.js и SSR
Пакет работает и вне браузера. В Node.js функции читают process.platform вместо navigator:
ts
import { getOS, detectIsWindows, detectIsWindows11 } from 'os-detect';
// Запущено на macOS-сервере
getOS(); // 'macos'
detectIsWindows(); // false
// Запущено на Windows-машине
getOS(); // 'windows'
detectIsWindows(); // true
// Windows 11 через os.release() (build >= 22000)
const isWin11 = await detectIsWindows11();
process.platform |
Определяется как |
|---|---|
darwin |
macOS |
win32 |
Windows |
linux |
Linux |
android |
Android |
Это удобно для CLI-утилит и скриптов, которые меняют поведение в зависимости от платформы разработчика.
Важно для SSR: в Next.js или Nuxt getOS() на сервере вернёт ОС сервера, а не клиента. Если нужно определить ОС пользователя — оберните вызов в useEffect (React) или onMounted (Vue), чтобы код выполнился уже в браузере.
TypeScript
Пакет написан на TypeScript, типы включены в поставку. Тип OS доступен для импорта:
ts
import type { OS } from 'os-detect';
function getDownloadUrl(os: OS): string {
switch (os) {
case 'windows': return '/download/windows.exe';
case 'macos': return '/download/macos.dmg';
case 'linux': return '/download/linux.AppImage';
default: return '/download';
}
}
Итог
os-detect решает одну задачу и решает её хорошо: определяет операционную систему там, где вы работаете — в браузере через userAgentData и userAgent, в Node.js через process.platform.