vue-composables
π―Skillfrom alexanderop/workouttracker
Generates high-quality Vue 3 composables by following established patterns and best practices for creating reusable Composition API logic.
Installation
npx skills add https://github.com/alexanderop/workouttracker --skill vue-composablesSkill Details
Write high-quality Vue 3 composables following established patterns and best practices. Use when creating new composables, refactoring existing ones, or reviewing composable code. Triggers include requests to "create a composable", "write a use* function", "extract logic into a composable", or any Vue Composition API reusable logic task.
Overview
# Vue 3 Composables Style Guide
Naming Conventions
Files
- Prefix with
useand use PascalCase:useCounter.ts,useApiRequest.ts - Place in
src/composables/directory
Functions
- Use descriptive names:
useUserData()notuseData() - Export as named function:
export function useCounter() {}
Structure Template
Follow this order consistently:
```ts
import { computed, onMounted, ref, watch } from 'vue'
export function useExample() {
// 1. Initializing - setup logic, router, external dependencies
// 2. Primary State - main reactive state
const data = ref(null)
// 3. State Metadata - status, errors, loading
const status = ref<'idle' | 'loading' | 'success' | 'error'>('idle')
const error = ref
// 4. Computed - derived state
const isLoading = computed(() => status.value === 'loading')
// 5. Methods - state manipulation
const fetchData = async () => {
status.value = 'loading'
try {
// fetch logic
status.value = 'success'
}
catch (e) {
status.value = 'error'
error.value = e instanceof Error ? e : new Error(String(e))
}
}
// 6. Lifecycle Hooks
onMounted(() => {
// initialization logic
})
// 7. Watchers
watch(data, (newValue) => {
// react to changes
})
return { data, status, error, isLoading, fetchData }
}
```
Core Rules
Single Responsibility
One composable = one purpose. Avoid mixing unrelated concerns.
```ts
// GOOD - focused on one task
export function useCounter() {
const count = ref(0)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
return { count, increment, decrement }
}
// BAD - mixing user data and counter
export function useUserAndCounter() {
const user = ref(null)
const count = ref(0)
// ... mixed concerns
}
```
Expose Error State
Return errors for component handling. Never swallow errors or show UI directly.
```ts
// GOOD
const error = ref
try {
await fetchData()
}
catch (e) {
error.value = e instanceof Error ? e : new Error(String(e))
}
return { error }
// BAD - swallowing errors or showing UI
try {
await fetchData()
}
catch (e) {
console.error(e) // swallowed
showToast('Error!') // UI in composable
}
```
No UI Logic in Composables
Keep composables focused on state/logic. Handle UI in components.
```ts
// GOOD - composable returns state
export function useUserData(userId: string) {
const user = ref
const error = ref
const fetchUser = async () => { / ... / }
return { user, error, fetchUser }
}
// Component handles UI
const { error } = useUserData(userId)
watch(error, (e) => {
if (e)
showToast('Error occurred')
})
```
Object Arguments for 4+ Parameters
```ts
// GOOD - object for many params
useUserData({ id: 1, fetchOnMount: true, token: 'abc', locale: 'en' })
// GOOD - positional for few params
useCounter(initialValue, step)
// BAD - too many positional args
useUserData(1, true, 'abc', 'en', false, 'default')
```
Group Related State into Objects
When a composable has 4+ related state properties, group them into a single ref object instead of separate refs.
```ts
// GOOD - grouped state for 4+ related properties
interface FormState {
name: string
email: string
phone: string
address: string
}
export function useContactForm() {
const form = ref
name: '',
email: '',
phone: '',
address: '',
})
function reset() {
form.value = { name: '', email: '', phone: '', address: '' }
}
return { form, reset }
}
// GOOD - separate refs for 1-3 unrelated properties
export function useToggle() {
const isOpen = ref(false)
const isLoading = ref(false)
return { isOpen, isLoading }
}
// BAD - many separate refs for related state
export function useContactForm() {
const name = ref('')
const email = ref('')
const phone = ref('')
const address = ref('')
// ... becomes unwieldy to manage and reset
}
```
Functional Core, Imperative Shell
Extract pure logic from Vue reactivity when beneficial.
```ts
// Pure function (testable, no side effects)
function calculateTotal(items: ReadonlyArray
return items.reduce((sum, item) => sum + item.price, 0)
}
// Composable uses the pure function
export function useCart() {
const items = ref
const total = computed(() => calculateTotal(items.value))
return { items, total }
}
```
Quick Reference
| Aspect | Do | Don't |
|--------|-----|-------|
| Naming | useUserData, useFetchApi | useData, getData |
| File | useCounter.ts in composables/ | counter.ts anywhere |
| Errors | Return error ref | console.error() or toast |
| UI | Return state, handle UI in component | showModal() in composable |
| Params | Object for 4+ params | Long positional arg lists |
| State | ref object for 4+ related properties | Many separate refs |
| Focus | Single responsibility | Mixed concerns |
More from this repository7
Tracks and automatically advances kettlebell swing progressions through reps, time, and weight phases using an EMOM timer.
Helps product owners and business analysts streamline product planning by creating, refining, and prioritizing user stories, PRDs, backlogs, and roadmaps.
vue-composable-testing skill from alexanderop/workouttracker
Enables adding new exercises to the workout tracker database by specifying exercise details like name, equipment, muscle group, type, and tracking metrics.
Enables comprehensive Vue 3 integration testing using Vitest Browser Mode and Page Objects for verifying complex user flows and multi-component interactions.
Provides comprehensive Vitest mocking strategies for creating test doubles, replacing dependencies, and verifying interactions in JavaScript/TypeScript tests.
Enhance mobile app accessibility by generating clear, context-aware notifications with screen reader compatibility and adaptive formatting.