Web
@nano_kit/platform-web provides small reactive wrappers around browser APIs. The package is built on top of @nano_kit/store, so every helper returns a signal that can be used with effect, computed, framework integrations, query settings, or your own store utilities.
Instead of wiring browser event listeners by hand, use these helpers when you want Web API state to participate in Nano Kit reactivity.
Installation
Section titled “Installation”Install the package using your favorite package manager:
pnpm add @nano_kit/store @nano_kit/platform-webyarn add @nano_kit/store @nano_kit/platform-webnpm install @nano_kit/store @nano_kit/platform-webQuick Start
Section titled “Quick Start”import { BooleanCodec, effect} from '@nano_kit/store'import { $networkOnline, $pageVisible, localStored, mediaQuery} from '@nano_kit/platform-web'
const $dark = localStored('dark', false, BooleanCodec)const $wide = mediaQuery('(min-width: 768px)', false)
const stop = effect(() => { console.log({ dark: $dark(), online: $networkOnline(), pageVisible: $pageVisible(), wide: $wide() })})
$dark(true)
stop()Storage
Section titled “Storage”localStored and sessionStored create writable signals backed by localStorage and sessionStorage.
import { BooleanCodec, debounce } from '@nano_kit/store'import { localStored, sessionStored } from '@nano_kit/platform-web'
const $dark = localStored('dark', false, BooleanCodec)const $draft = sessionStored('draft', '', debounce(300))
$dark(true)$draft('Hello')syncedLocalStored and syncedSessionStored also listen for storage events, so the signal can react to changes from other browsing contexts.
import { effect } from '@nano_kit/store'import { syncedLocalStored } from '@nano_kit/platform-web'
const $language = syncedLocalStored('language', 'en')
const stop = effect(() => { document.documentElement.lang = $language()})All storage helpers support the same optional arguments as stored: default values, codecs, and setter rate limiters.
Media Queries
Section titled “Media Queries”mediaQuery wraps window.matchMedia(...).
import { mediaQuery } from '@nano_kit/platform-web'
const $wide = mediaQuery('(min-width: 768px)', false)const $reducedMotion = mediaQuery('(prefers-reduced-motion: reduce)', false)The optional second argument is the fallback value used when window is not available.
Browser Properties
Section titled “Browser Properties”The package exports shared singleton signals for common browser state.
import { $devicePixelRatio, $fullscreen, $innerHeight, $innerWidth, $networkOnline, $outerHeight, $outerWidth, $pageVisible, $screenLeft, $screenOrientation, $screenTop, $scrollX, $scrollY} from '@nano_kit/platform-web'Window size and position:
$innerWidth$innerHeight$outerWidth$outerHeight$scrollX$scrollY$screenLeft$screenTop$devicePixelRatio
Page and device state:
$networkOnline$pageVisible$screenOrientation$fullscreen
Numeric window values use NaN when the browser value is not available. Boolean values use browser-friendly fallbacks: $networkOnline and $pageVisible start as true, and $fullscreen starts as false.
Permissions
Section titled “Permissions”permission creates a writable signal for the current Permissions API state. The value is 'granted', 'denied', 'prompt', Error, or undefined before the first result.
import { effect } from '@nano_kit/store'import { permission } from '@nano_kit/platform-web'
const $geolocationPermission = permission('geolocation')
const stop = effect(() => { const state = $geolocationPermission()
if (state === 'granted') { console.log('Geolocation is available') }})The argument can be a permission name string or a full PermissionDescriptor.
Geolocation
Section titled “Geolocation”$geolocation is backed by navigator.geolocation.watchPosition(...) and updates while the signal is active.
$staticGeolocation is backed by navigator.geolocation.getCurrentPosition(...) and reads a single snapshot when the signal starts.
import { effect } from '@nano_kit/store'import { $geolocation } from '@nano_kit/platform-web'
const stopLive = effect(() => { const result = $geolocation()
if (result && 'coords' in result) { console.log(result.coords.latitude, result.coords.longitude) }})Both signals can contain GeolocationPosition, GeolocationPositionError, or undefined.
Cookies
Section titled “Cookies”cookieStored and syncedCookieStored adapt a CookieStore-compatible object to Nano Kit storage signals.
import { JsonCodec } from '@nano_kit/store'import { cookieStored, syncedCookieStored } from '@nano_kit/platform-web'
const $theme = cookieStored(cookieStore, { name: 'theme', maxAge: 60 * 60 * 24 * 30, path: '/'}, 'light')
const $profile = syncedCookieStored(cookieStore, 'profile', {}, JsonCodec)
$theme('dark')Use cookieStored when you only need reads and writes. Use syncedCookieStored when the cookie store supports change events and you want the signal to react to external cookie changes.
Broadcast Channel
Section titled “Broadcast Channel”broadcasted creates a signal synchronized through BroadcastChannel. It is useful for transient cross-tab messages such as logout, refresh, or UI coordination events.
import { effect } from '@nano_kit/store'import { broadcasted } from '@nano_kit/platform-web'
const $authEvent = broadcasted<'logout' | 'refresh'>('auth')
const stop = effect(() => { if ($authEvent() === 'logout') { console.log('Log out this tab') }})
$authEvent('logout')broadcasted supports the same default value, codec, and setter rate limiter overloads as the storage helpers.