Composables
A composable is a function that uses Vue's reactivity (ref, computed, lifecycle hooks) to package up reusable, stateful logic — exactly what React custom hooks do, and what Vue calls its own version of the pattern. There's no special syntax: it's just a plain function, by convention named useSomething, that calls reactivity APIs inside and returns reactive values.
A First Composable — useCounter
useCounter bundles a ref and its behaviour into one reusable function, returning the reactive count and the functions to change it. Any component imports it and destructures what it needs — and crucially, each call creates its own independent state, just like calling a React hook. This maps directly onto React's useToggle from Intermediate Chapter 4; the only real syntax difference is returning an object (so you destructure by name) rather than a tuple.
A Composable with Lifecycle — useMousePosition
This is the real payoff teased in Chapter 9: the onMounted/onUnmounted listener-pairing — the kind of thing you'd otherwise repeat in every component needing mouse position — lives in one composable. A composable can call lifecycle hooks itself, and Vue ties them to whichever component is using it. Any component just does const { x, y } = useMousePosition() and gets a self-cleaning, reactive position — the setup and teardown handled automatically.
useLocalStorage — A Persisted ref
The same useLocalStorage that was a React custom hook (Intermediate Chapter 4) and an Angular signal+effect (Angular Chapter 13) — here, a composable combining a ref initialized from storage with a watch that re-saves on every change. const notes = useLocalStorage('notes', []) returns a normal-looking ref that transparently persists. The { deep: true } option makes the watch fire on nested changes inside objects/arrays, not just reassignment.
mixins: option in old code, a composable is the modern replacement.
refs themselves (or a reactive object), not ref.value. Returning count.value hands back a plain number — a one-time snapshot that won't stay reactive in the component. Return count (the ref) so the component keeps the live, reactive connection. This is the composable equivalent of the same care React takes to return state and its setter, not a stale value.
| Vue composable | React custom hook | Angular equivalent |
|---|---|---|
| useCounter() | useToggle() / custom hook | Injectable service |
| Returns an object (destructure) | Returns a tuple/object | Class instance |
| Can call onMounted/onUnmounted | Can call useEffect | Lifecycle in the service host |
| Each call = isolated state | Each call = isolated state | One shared instance (singleton) |
Coding Challenges
Write a useCounter composable returning { count, increment, decrement, reset }, and use it in two separate components — confirming each gets its own independent count.
📄 View solutionWrite a useWindowWidth composable that tracks window.innerWidth via a resize listener added in onMounted and removed in onUnmounted, returning the reactive width — then use it in a component, confirming the listener cleans up when the component is removed.
📄 View solutionWrite a useLocalStorage composable that returns a ref initialized from localStorage and persists it on change via a watch, then use it to persist a counter's value so it survives a page refresh.
📄 View solutionChapter 10 Quick Reference
- A composable is a function (conventionally
useX) packaging reusable, stateful reactive logic — Vue's custom hook - No special syntax — call
ref/computed/lifecycle hooks inside, return reactive values - Returns an object (destructure by name); each call gets isolated state
- A composable can call
onMounted/onUnmounted— bundling setup+teardown in one place - Return the refs, not
ref.value, to keep the reactive connection live - Composables replace Vue 2 mixins, for the same reasons hooks replaced HOCs/mixins in React
- Next chapter: Vue Router — multiple pages in a single-page app