Vue Composables Development
When to Apply
Activate this skill when:
- Creating custom composables
- Using Composition API features (ref, reactive, computed)
- Working with watchers and lifecycle hooks
- Implementing provide/inject patterns
- Managing component state with Composition API
Documentation
Use search-docs for detailed Vue 3 Composition API patterns and documentation.
Basic Usage
Refs and Reactive
// Ref for primitives const count = ref(0); count.value++; // Access with .value
// Reactive for objects const state = reactive({ name: '', items: [], }); state.name = 'Updated'; // Direct access
// Computed const doubled = computed(() => count.value * 2);
Custom Composables
Create reusable logic with composables:
export function useCounter(initial = 0) { const count = ref(initial);
const doubled = computed(() => count.value * 2);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
return {
count,
doubled,
increment,
decrement,
};
}
Watchers
const search = ref('');
// Watch specific ref
watch(search, (newValue, oldValue) => {
console.log(Search changed from ${oldValue} to ${newValue});
});
// Watch with options watch(search, (value) => { fetchResults(value); }, { immediate: true, debounce: 300 });
// Watch multiple sources
watch([firstName, lastName], ([first, last]) => {
fullName.value = ${first} ${last};
});
// watchEffect - auto-tracks dependencies
watchEffect(() => {
console.log(Count is: ${count.value});
});
Lifecycle Hooks
onBeforeMount(() => { // Before component mounts });
onMounted(() => { // Component mounted - DOM available window.addEventListener('resize', handleResize); });
onUnmounted(() => { // Cleanup window.removeEventListener('resize', handleResize); });
Async Composables
export function useFetch(url: string) { const data = ref<T | null>(null); const error = ref<Error | null>(null); const loading = ref(false);
async function execute() {
loading.value = true;
error.value = null;
try {
const response = await fetch(url);
data.value = await response.json();
} catch (e) {
error.value = e as Error;
} finally {
loading.value = false;
}
}
execute();
return { data, error, loading, refetch: execute };
}
Provide/Inject
const theme = ref('dark'); provide('theme', theme);
// Child component (any depth) import { inject } from 'vue';
const theme = inject('theme', ref('light')); // With default
Type-Safe Composables
interface UseModalReturn { isOpen: Ref; open: () => void; close: () => void; toggle: () => void; }
export function useModal(): UseModalReturn { const isOpen = ref(false);
return {
isOpen,
open: () => (isOpen.value = true),
close: () => (isOpen.value = false),
toggle: () => (isOpen.value = !isOpen.value),
};
}
Composable Conventions
- Name composables with
useprefix:useCounter,useFetch - Place in
composables/directory - Return refs, not raw values (maintains reactivity)
- Document parameters and return types
- Clean up side effects in
onUnmounted
Common Pitfalls
- Destructuring reactive objects (loses reactivity) - use
toRefs() - Forgetting
.valuewhen accessing refs in script - Not cleaning up event listeners or intervals in
onUnmounted - Creating composables that don't return reactive references
- Using
refwhenreactiveis more appropriate for complex objects
