Vue Composables Pattern
Extract and reuse stateful logic across components using Vue composables
When to Use
- Multiple components share the same stateful logic (e.g., window size tracking, API fetching, form validation)
- You want to extract complex logic out of a component's
<script setup> for readability and reuse
- Replacing Vue 2 mixins with a more explicit, composable approach
Instructions
- Create a function prefixed with
use (e.g., useWindowSize, useFetch).
- Inside, use
ref, reactive, computed, watch, and lifecycle hooks as needed.
- Return only what consumers need — keep internal state private.
- Co-locate composable files with their primary consumer or place in a
composables/ directory.
// composables/useWindowSize.ts
import { ref, onMounted, onUnmounted } from 'vue';
export function useWindowSize() {
const width = ref(window.innerWidth);
const height = ref(window.innerHeight);
function update() {
width.value = window.innerWidth;
height.value = window.innerHeight;
}
onMounted(() => window.addEventListener('resize', update));
onUnmounted(() => window.removeEventListener('resize', update));
return { width, height };
}
- Composables can accept parameters (refs or plain values) for configuration.
- Return cleanup functions or use lifecycle hooks to handle side-effect teardown.
Details
Composables are the Vue 3 Composition API's answer to code reuse. They replace mixins, which suffered from namespace collisions, implicit dependencies, and unclear data sources. A composable is a plain function that uses Vue's reactivity primitives, making dependencies explicit.
Trade-offs:
- Composables must be called during
setup() — they cannot be used conditionally or in loops (similar to React hooks rules)
- Deeply nested composable calls can make the dependency chain hard to trace
- Over-extracting into composables can fragment logic that is easier to understand inline
When NOT to use:
- For stateless utility functions — just export a plain function, no need for Vue reactivity
- When the logic is used by only one component — inline it in
<script setup> until reuse is needed
- For global state — use Pinia stores instead of a composable with module-level state
Source
https://patterns.dev/vue/composables-pattern
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.
1---2name: vue-composables-pattern3description: Vue Composables Pattern4---5# Vue Composables Pattern67> Extract and reuse stateful logic across components using Vue composables89## When to Use1011- Multiple components share the same stateful logic (e.g., window size tracking, API fetching, form validation)12- You want to extract complex logic out of a component's `<script setup>` for readability and reuse13- Replacing Vue 2 mixins with a more explicit, composable approach1415## Instructions16171. Create a function prefixed with `use` (e.g., `useWindowSize`, `useFetch`).182. Inside, use `ref`, `reactive`, `computed`, `watch`, and lifecycle hooks as needed.193. Return only what consumers need — keep internal state private.204. Co-locate composable files with their primary consumer or place in a `composables/` directory.2122```typescript23// composables/useWindowSize.ts24import { ref, onMounted, onUnmounted } from 'vue';2526export function useWindowSize() {27 const width = ref(window.innerWidth);28 const height = ref(window.innerHeight);2930 function update() {31 width.value = window.innerWidth;32 height.value = window.innerHeight;33 }3435 onMounted(() => window.addEventListener('resize', update));36 onUnmounted(() => window.removeEventListener('resize', update));3738 return { width, height };39}40```41425. Composables can accept parameters (refs or plain values) for configuration.436. Return cleanup functions or use lifecycle hooks to handle side-effect teardown.4445## Details4647Composables are the Vue 3 Composition API's answer to code reuse. They replace mixins, which suffered from namespace collisions, implicit dependencies, and unclear data sources. A composable is a plain function that uses Vue's reactivity primitives, making dependencies explicit.4849**Trade-offs:**5051- Composables must be called during `setup()` — they cannot be used conditionally or in loops (similar to React hooks rules)52- Deeply nested composable calls can make the dependency chain hard to trace53- Over-extracting into composables can fragment logic that is easier to understand inline5455**When NOT to use:**5657- For stateless utility functions — just export a plain function, no need for Vue reactivity58- When the logic is used by only one component — inline it in `<script setup>` until reuse is needed59- For global state — use Pinia stores instead of a composable with module-level state6061## Source6263https://patterns.dev/vue/composables-pattern6465## Process66671. Read the instructions and examples in this document.682. Apply the patterns to your implementation, adapting to your specific context.693. Verify your implementation against the details and edge cases listed above.7071## Harness Integration7273- **Type:** knowledge — this skill is a reference document, not a procedural workflow.74- **No tools or state** — consumed as context by other skills and agents.7576## Success Criteria7778- The patterns described in this document are applied correctly in the implementation.79- Edge cases and anti-patterns listed in this document are avoided.