Stacks Composables
154 reactive composables for STX templates. A fixed set of them is available
bare, listed below; everything else needs an explicit import from
@stacksjs/composables.
The stx runtime decides this, not browser-auto-imports.json. That manifest
feeds an ambient .d.ts and nothing reads it at build time, so it says what the
compiler accepts and not what the browser has; the two disagree in both
directions (stacksjs/stacks#2585).
This page said "All are auto-imported in STX templates", which is the mistake
AGENTS.md carries a scar about under "200+ composables": an agent reaching for
a name on that authority writes a template that does not run, and reads the
failure as a framework bug.
What you can write bare in a template
useAsync, useClickOutside, useColorMode, useCounter, useDark,
useDebounce, useDebouncedValue, useEventListener, useFetch, useFocus,
useHead, useInterval, useLocalStorage, useMutation, useQuery,
useRef, useRoute, useSearchParams, useSeoMeta, useSessionStorage,
useStore, useThrottle, useTimeout, useToggle, useWebSocket.
Everything else needs an explicit import, and that is most of what the sections
below list:
import { useStorage } from '@stacksjs/composables'
buddy typecheck will not tell you which is which, and currently disagrees
with the browser in both directions (stacksjs/stacks#2585).
storage/framework/browser-auto-imports.json feeds an ambient .d.ts, so the
compiler accepts every name it declares - and only five of its 27 use* are in
the runtime. useStorage, useNow, useDateFormat, useForm and the use*Store
composables typecheck and then throw a ReferenceError during setup, which takes
the page down rather than failing the one call. In the other direction
useLocalStorage, useColorMode, useCounter and useMediaQuery all work in
a template and tsc rejects them.
The list above is the runtime's, so it is the one that predicts whether the page
loads.
Key Path
- Core package:
storage/framework/core/composables/src/
Core Reactive Primitives
// From _shared.ts
type MaybeRef<T> = T | Ref<T>
type MaybeRefOrGetter<T> = T | Ref<T> | (() => T)
unref(val) // unwrap Ref
toValue(val) // unwrap Ref or getter
isRef(val) // type guard
State & Reactivity
useToggle(initial?) → [Ref<boolean>, toggle]
useCounter(initial?) → { count, increment, decrement, set, reset }
useStepper(steps, initial?) → step navigation
usePrevious(value) → previous value
useCycleList(list) → cycle through items
Storage
useStorage(key, defaultValue, storage?) → persistent Ref
useLocalStorage(key, defaultValue) → localStorage-backed Ref
useSessionStorage(key, defaultValue) → sessionStorage-backed Ref
Time & Date
useNow(options?) → Ref<Date> (auto-updating)
useDateFormat(date, format) → Ref<string>
useTimeAgo(date) → relative time string
useTimestamp(options?) → Ref<number>
useInterval(fn, ms) → interval control
useIntervalFn(fn, ms) → interval with pause/resume
useTimeout(ms) → timeout control
useTimeoutFn(fn, ms) → delayed execution
DOM & Browser
useWindowSize() → { width, height }
useWindowScroll() → { x, y }
useWindowFocus() → Ref<boolean>
useDocumentVisibility() → Ref<string>
useFullscreen(el?) → { isFullscreen, enter, exit, toggle }
useTitle(title) → document title binding
useFavicon(url) → favicon binding
useCssVar(prop, el?) → CSS variable binding
useActiveElement() → currently focused element
useTextSelection() → selected text
useTextDirection() → Ref<'ltr' | 'rtl'>
useNavigatorLanguage() → browser language
Mouse & Touch
useMouse() → { x, y, sourceType }
useMouseInElement(el) → mouse position relative to element
useMousePressed() → { pressed, sourceType }
usePointer() → pointer events
useSwipe(el) → swipe detection
usePointerSwipe(el) → pointer swipe
useDraggable(el) → make element draggable
useDropZone(el) → drop zone detection
onLongPress(el, handler) → long press detection
onClickOutside(el, handler) → click outside detection
Sensors
useGeolocation() → { coords, locatedAt, error }
useDeviceMotion() → acceleration & rotation
useDeviceOrientation() → alpha, beta, gamma
useBattery() → { charging, chargingTime, level }
useDevicePixelRatio() → Ref<number>
useScreenSafeArea() → safe area insets
Observers
useIntersectionObserver(el, callback) → visibility detection
useResizeObserver(el, callback) → size changes
useMutationObserver(el, callback) → DOM mutations
useElementBounding(el) → { top, left, width, height }
useElementVisibility(el) → Ref<boolean>
useElementHover(el) → Ref<boolean>
Async
useAsyncState(fn, initial) → { state, isReady, isLoading, error, execute }
useAsyncQueue(tasks) → sequential async execution
computedAsync(fn) → async computed value
computedEager(fn) → immediately evaluated computed
Network
useFetch(url, options?) → fetch wrapper with reactive state
useWebSocket(url) → WebSocket connection
useEventSource(url) → SSE connection
useOnline() → Ref<boolean> (network status)
Input & Focus
useFocus(el) → { focused, focus, blur }
useFocusWithin(el) → any child focused
useKeyModifier(key) → modifier key state
usePermission(name) → permission state
useShare(options) → Web Share API
Utilities
useDebounceFn(fn, ms) → debounced function
useThrottleFn(fn, ms) → throttled function
useDebouncedRef(ref, ms) → debounced ref updates
useThrottledRef(ref, ms) → throttled ref updates
watchDebounced(source, callback, ms) → debounced watcher
watchThrottled(source, callback, ms) → throttled watcher
watchOnce(source, callback) → one-time watcher
whenever(source, callback) → watch for truthy
until(source).toBe(value) → wait for value
syncRef(refA, refB) → bidirectional sync
Dark Mode
useDark() → Ref<boolean>
usePreferredDark() → system preference
usePreferredColorScheme() → color scheme preference
Media
useMediaQuery(query) → Ref<boolean>
usePreferredContrast() → contrast preference
usePreferredLanguages() → language preferences
usePreferredReducedMotion() → reduce motion preference
State Patterns
createEventHook() → typed event hook
createGlobalState(fn) → shared state across components
createSharedComposable(fn) → shared composable instance
refDefault(ref, defaultValue) → ref with default
refAutoReset(value, ms) → auto-resetting ref
makeDestructurable(obj, arr) → support both destructuring styles
useIdle(ms) → user idle detection
usePageLeave() → detect page leave
useFps() → frames per second
useMounted() → Ref<boolean> mount state
tryOnMounted(fn) → safe onMounted
useObjectUrl(blob) → object URL with auto-cleanup
Script & Style Injection
useScriptTag(src, onLoaded?) → inject <script>
useStyleTag(css) → inject <style>
Math
useAbs, useAverage, useCeil, useClamp, useFloor, useMax, useMin, usePrecision, useRound, useSum, useTrunc
and, or, logicNot, logicOr
Gotchas
- Only the names listed above are available bare in an STX template, and they
come from the stx runtime, not from
browser-auto-imports.json - that
manifest is compile-time only and disagrees with the runtime in both
directions (stacksjs/stacks#2585). Everything else needs
import { … } from '@stacksjs/composables'
- NEVER use vanilla JS (
var, document.*, window.*) in STX <script> tags
- Only use stx-compatible code: signals, composables, directives
- Auto-imports defined in
storage/framework/browser-auto-imports.json
- Many composables require a browser environment (won't work server-side)
useStorage persists to localStorage by default
1---2name: stacks-composables-33description: Use when creating or using reactive composables in STX templates - 154 composables for state management, DOM interaction, sensors, animation, browser APIs, async operations, or the complete list of auto-imported composables. Covers @stacksjs/composables.4license: MIT5---67# Stacks Composables89154 reactive composables for STX templates. **A fixed set of them is available10bare**, listed below; everything else needs an explicit import from11`@stacksjs/composables`.1213The stx runtime decides this, not `browser-auto-imports.json`. That manifest14feeds an ambient `.d.ts` and nothing reads it at build time, so it says what the15compiler accepts and not what the browser has; the two disagree in both16directions (stacksjs/stacks#2585).1718This page said "All are auto-imported in STX templates", which is the mistake19`AGENTS.md` carries a scar about under "200+ composables": an agent reaching for20a name on that authority writes a template that does not run, and reads the21failure as a framework bug.2223## What you can write bare in a template2425<!-- auto-imported:begin - checked against the stx runtime by26 core/composables/tests/skill-runtime-globals.test.ts. These are the names27 `getCachedSignalsRuntime()` attaches to `window`, which is what decides28 whether a bare call resolves in a template. Do not derive this list from29 `browser-auto-imports.json`: that manifest is compile-time only, and 22 of30 the 27 `use*` it declares are absent from the runtime. -->3132`useAsync`, `useClickOutside`, `useColorMode`, `useCounter`, `useDark`,33`useDebounce`, `useDebouncedValue`, `useEventListener`, `useFetch`, `useFocus`,34`useHead`, `useInterval`, `useLocalStorage`, `useMutation`, `useQuery`,35`useRef`, `useRoute`, `useSearchParams`, `useSeoMeta`, `useSessionStorage`,36`useStore`, `useThrottle`, `useTimeout`, `useToggle`, `useWebSocket`.3738<!-- auto-imported:end -->3940Everything else needs an explicit import, and that is most of what the sections41below list:4243```ts44import { useStorage } from '@stacksjs/composables'45```4647**`buddy typecheck` will not tell you which is which, and currently disagrees48with the browser in both directions** (stacksjs/stacks#2585).49`storage/framework/browser-auto-imports.json` feeds an ambient `.d.ts`, so the50compiler accepts every name it declares - and only five of its 27 `use*` are in51the runtime. `useStorage`, `useNow`, `useDateFormat`, `useForm` and the `use*Store`52composables typecheck and then throw a ReferenceError during setup, which takes53the page down rather than failing the one call. In the other direction54`useLocalStorage`, `useColorMode`, `useCounter` and `useMediaQuery` all work in55a template and `tsc` rejects them.5657The list above is the runtime's, so it is the one that predicts whether the page58loads.5960## Key Path61- Core package: `storage/framework/core/composables/src/`6263## Core Reactive Primitives6465```typescript66// From _shared.ts67type MaybeRef<T> = T | Ref<T>68type MaybeRefOrGetter<T> = T | Ref<T> | (() => T)69unref(val) // unwrap Ref70toValue(val) // unwrap Ref or getter71isRef(val) // type guard72```7374## State & Reactivity75- `useToggle(initial?)` → `[Ref<boolean>, toggle]`76- `useCounter(initial?)` → `{ count, increment, decrement, set, reset }`77- `useStepper(steps, initial?)` → step navigation78- `usePrevious(value)` → previous value79- `useCycleList(list)` → cycle through items8081## Storage82- `useStorage(key, defaultValue, storage?)` → persistent Ref83- `useLocalStorage(key, defaultValue)` → localStorage-backed Ref84- `useSessionStorage(key, defaultValue)` → sessionStorage-backed Ref8586## Time & Date87- `useNow(options?)` → `Ref<Date>` (auto-updating)88- `useDateFormat(date, format)` → `Ref<string>`89- `useTimeAgo(date)` → relative time string90- `useTimestamp(options?)` → `Ref<number>`91- `useInterval(fn, ms)` → interval control92- `useIntervalFn(fn, ms)` → interval with pause/resume93- `useTimeout(ms)` → timeout control94- `useTimeoutFn(fn, ms)` → delayed execution9596## DOM & Browser97- `useWindowSize()` → `{ width, height }`98- `useWindowScroll()` → `{ x, y }`99- `useWindowFocus()` → `Ref<boolean>`100- `useDocumentVisibility()` → `Ref<string>`101- `useFullscreen(el?)` → `{ isFullscreen, enter, exit, toggle }`102- `useTitle(title)` → document title binding103- `useFavicon(url)` → favicon binding104- `useCssVar(prop, el?)` → CSS variable binding105- `useActiveElement()` → currently focused element106- `useTextSelection()` → selected text107- `useTextDirection()` → `Ref<'ltr' | 'rtl'>`108- `useNavigatorLanguage()` → browser language109110## Mouse & Touch111- `useMouse()` → `{ x, y, sourceType }`112- `useMouseInElement(el)` → mouse position relative to element113- `useMousePressed()` → `{ pressed, sourceType }`114- `usePointer()` → pointer events115- `useSwipe(el)` → swipe detection116- `usePointerSwipe(el)` → pointer swipe117- `useDraggable(el)` → make element draggable118- `useDropZone(el)` → drop zone detection119- `onLongPress(el, handler)` → long press detection120- `onClickOutside(el, handler)` → click outside detection121122## Sensors123- `useGeolocation()` → `{ coords, locatedAt, error }`124- `useDeviceMotion()` → acceleration & rotation125- `useDeviceOrientation()` → alpha, beta, gamma126- `useBattery()` → `{ charging, chargingTime, level }`127- `useDevicePixelRatio()` → `Ref<number>`128- `useScreenSafeArea()` → safe area insets129130## Observers131- `useIntersectionObserver(el, callback)` → visibility detection132- `useResizeObserver(el, callback)` → size changes133- `useMutationObserver(el, callback)` → DOM mutations134- `useElementBounding(el)` → `{ top, left, width, height }`135- `useElementVisibility(el)` → `Ref<boolean>`136- `useElementHover(el)` → `Ref<boolean>`137138## Async139- `useAsyncState(fn, initial)` → `{ state, isReady, isLoading, error, execute }`140- `useAsyncQueue(tasks)` → sequential async execution141- `computedAsync(fn)` → async computed value142- `computedEager(fn)` → immediately evaluated computed143144## Network145- `useFetch(url, options?)` → fetch wrapper with reactive state146- `useWebSocket(url)` → WebSocket connection147- `useEventSource(url)` → SSE connection148- `useOnline()` → `Ref<boolean>` (network status)149150## Input & Focus151- `useFocus(el)` → `{ focused, focus, blur }`152- `useFocusWithin(el)` → any child focused153- `useKeyModifier(key)` → modifier key state154- `usePermission(name)` → permission state155- `useShare(options)` → Web Share API156157## Utilities158- `useDebounceFn(fn, ms)` → debounced function159- `useThrottleFn(fn, ms)` → throttled function160- `useDebouncedRef(ref, ms)` → debounced ref updates161- `useThrottledRef(ref, ms)` → throttled ref updates162- `watchDebounced(source, callback, ms)` → debounced watcher163- `watchThrottled(source, callback, ms)` → throttled watcher164- `watchOnce(source, callback)` → one-time watcher165- `whenever(source, callback)` → watch for truthy166- `until(source).toBe(value)` → wait for value167- `syncRef(refA, refB)` → bidirectional sync168169## Dark Mode170- `useDark()` → `Ref<boolean>`171- `usePreferredDark()` → system preference172- `usePreferredColorScheme()` → color scheme preference173174## Media175- `useMediaQuery(query)` → `Ref<boolean>`176- `usePreferredContrast()` → contrast preference177- `usePreferredLanguages()` → language preferences178- `usePreferredReducedMotion()` → reduce motion preference179180## State Patterns181- `createEventHook()` → typed event hook182- `createGlobalState(fn)` → shared state across components183- `createSharedComposable(fn)` → shared composable instance184- `refDefault(ref, defaultValue)` → ref with default185- `refAutoReset(value, ms)` → auto-resetting ref186- `makeDestructurable(obj, arr)` → support both destructuring styles187- `useIdle(ms)` → user idle detection188- `usePageLeave()` → detect page leave189- `useFps()` → frames per second190- `useMounted()` → `Ref<boolean>` mount state191- `tryOnMounted(fn)` → safe onMounted192- `useObjectUrl(blob)` → object URL with auto-cleanup193194## Script & Style Injection195- `useScriptTag(src, onLoaded?)` → inject `<script>`196- `useStyleTag(css)` → inject `<style>`197198## Math199- `useAbs`, `useAverage`, `useCeil`, `useClamp`, `useFloor`, `useMax`, `useMin`, `usePrecision`, `useRound`, `useSum`, `useTrunc`200- `and`, `or`, `logicNot`, `logicOr`201202## Gotchas203- Only the names listed above are available bare in an STX template, and they204 come from the stx runtime, not from `browser-auto-imports.json` - that205 manifest is compile-time only and disagrees with the runtime in both206 directions (stacksjs/stacks#2585). Everything else needs207 `import { … } from '@stacksjs/composables'`208- NEVER use vanilla JS (`var`, `document.*`, `window.*`) in STX `<script>` tags209- Only use stx-compatible code: signals, composables, directives210- Auto-imports defined in `storage/framework/browser-auto-imports.json`211- Many composables require a browser environment (won't work server-side)212- `useStorage` persists to localStorage by default