Skip to content
Back to skills

Stacks Composables

ASecurity

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.

  • 631 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 5, 2026
developmenttypescriptgobashreactapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 20, 2026

npx -y skills add stacksjs/stacks --skill stacks-composables --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Stacks Composables?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Stacks Composables
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/stacksjs-stacks-composables/badge)](https://www.skillsdirectory.com/skills/stacksjs-stacks-composables)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: stacks-composables
description: 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.
license: MIT
compatibility: Bun >= 1.3.0, TypeScript
allowed-tools: Read Edit Write Bash Grep Glob
---

# 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

<!-- auto-imported:begin - checked against the stx runtime by
     core/composables/tests/skill-runtime-globals.test.ts. These are the names
     `getCachedSignalsRuntime()` attaches to `window`, which is what decides
     whether a bare call resolves in a template. Do not derive this list from
     `browser-auto-imports.json`: that manifest is compile-time only, and 22 of
     the 27 `use*` it declares are absent from the runtime. -->

`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`.

<!-- auto-imported:end -->

Everything else needs an explicit import, and that is most of what the sections
below list:

```ts
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

```typescript
// 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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.