Vue Composables
Import from os-detect/vue. Requires Vue 3+.
import { useOS, useIsWindows11 } from 'os-detect/vue'useOS()
- Takes: nothing
- Returns:
Readonly<Ref<OS>>— computed once viareadonly(ref(getOS()))
Returns a readonly Ref with the current OS string.
<script setup lang="ts">
import { useOS } from 'os-detect/vue'
const os = useOS() // Readonly<Ref<OS>>
</script>
<template>
<p>Running on {{ os }}</p>
</template>useIsWindows11()
- Takes: nothing
- Returns:
Readonly<Ref<boolean | null>>— starts asnull, resolves insideonMounted
Starts the async detectIsWindows11() check inside onMounted and updates the ref when it resolves.
<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>
<p v-if="isWin11 === null">Detecting Windows version…</p>
<p v-else-if="isWin11">Windows 11</p>
<p v-else-if="os === 'windows'">Windows 10 or older</p>
<p v-else>OS: {{ os }}</p>
</template>SSR (Nuxt)
useOS() calls getOS() eagerly inside ref(getOS()), in the composable's own function body — that runs during setup(), which executes on the server during SSR too, not only on the client. This means useOS() carries the same hydration-mismatch risk as calling getOS() directly: if the server's OS differs from the client's, the value baked into the server-rendered HTML won't match what the client recomputes on hydration.
useIsWindows11() does not have this problem — it deliberately starts at null and only resolves inside onMounted, which never runs during SSR.
If OS-dependent content must exactly match on the client, apply the same ref + onMounted pattern manually instead of useOS():
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getOS } from 'os-detect'
import type { OS } from 'os-detect'
const os = ref<OS | null>(null)
onMounted(() => {
os.value = getOS()
})
</script>
<template>
<p v-if="os">OS: {{ os }}</p>
</template>