Composables Reference
December 3, 2025 ยท View on GitHub
This document provides a comprehensive guide to the custom composables available in this project.
Table of Contents
useDrawer
Manages drawer/sidebar state with toggle functionality.
Usage
import { useDrawer } from '@/composables';
const { state, toggle, isExpanded } = useDrawer(initialWidth?, collapsedWidth?);
Parameters
initialWidth(optional): Initial width when expanded (default: 256)collapsedWidth(optional): Width when collapsed (default: 64)
Returns
state: Reactive state objectrail: Boolean indicating if drawer is in rail moderailWidth: Current width of the drawericon: Icon name for toggle button
toggle(): Function to toggle drawer stateisExpanded: Computed boolean indicating if drawer is expanded
Example
<script setup lang="ts">
import { useDrawer } from '@/composables';
const { state, toggle, isExpanded } = useDrawer();
</script>
<template>
<VNavigationDrawer
:rail-width="state.railWidth"
:rail="state.rail"
>
<VBtn :icon="state.icon" @click="toggle" />
</VNavigationDrawer>
</template>
useApi
Generic API call handler with loading and error states.
Usage
import { useApi, useGet, usePost, usePut, useDelete } from '@/composables';
// Generic
const { data, loading, error, execute } = useApi(url, config?, options?);
// HTTP method shortcuts
const { data, loading, error, execute } = useGet(url, options?);
const { data, loading, error, execute } = usePost(url, options?);
const { data, loading, error, execute } = usePut(url, options?);
const { data, loading, error, execute } = useDelete(url, options?);
Parameters
url: String or function returning URLconfig(optional): Axios request configurationoptions(optional):immediate: Execute request immediately (default: false)onSuccess: Callback on successful requestonError: Callback on error
Returns
data: Ref containing response dataloading: Ref boolean indicating loading stateerror: Ref containing error if request failedexecute(): Function to execute the request
Examples
GET Request with Immediate Execution
const { data, loading, error } = useGet('/api/users', {
immediate: true,
onSuccess: (data) => {
console.log('Users loaded:', data);
}
});
POST Request
const { execute: createUser, loading } = usePost('/api/users');
const handleSubmit = async () => {
try {
await createUser({
data: {
name: 'John Doe',
email: 'john@example.com'
}
});
} catch (error) {
console.error('Failed to create user');
}
};
Dynamic URL
const userId = ref(1);
const { data, execute } = useGet(() => `/api/users/${userId.value}`);
// Fetch different user
userId.value = 2;
await execute();
useFormValidation
Provides common form validation rules.
Usage
import { useFormValidation } from '@/composables';
const { isValid, rules } = useFormValidation();
Returns
isValid: Ref boolean for form validityrules: Object containing validation rule factories
Available Rules
required(message?)
Validates that field has a value.
:rules="[rules.required()]"
:rules="[rules.required('Name is required')]"
email(message?)
Validates email format.
:rules="[rules.email()]"
:rules="[rules.email('Please enter a valid email')]"
minLength(min, message?)
Validates minimum string length.
:rules="[rules.minLength(8)]"
:rules="[rules.minLength(8, 'Password must be at least 8 characters')]"
maxLength(max, message?)
Validates maximum string length.
:rules="[rules.maxLength(100)]"
:rules="[rules.maxLength(100, 'Maximum 100 characters allowed')]"
numeric(message?)
Validates that value is numeric.
:rules="[rules.numeric()]"
:rules="[rules.numeric('Please enter a number')]"
url(message?)
Validates URL format.
:rules="[rules.url()]"
:rules="[rules.url('Please enter a valid URL')]"
match(compareValue, message?)
Validates that value matches another value.
const password = ref('');
const confirmPassword = ref('');
:rules="[rules.match(password.value, 'Passwords must match')]"
Complete Form Example
<script setup lang="ts">
import { ref } from 'vue';
import { useFormValidation } from '@/composables';
const { rules } = useFormValidation();
const form = ref();
const email = ref('');
const password = ref('');
const confirmPassword = ref('');
const handleSubmit = async () => {
const { valid } = await form.value.validate();
if (valid) {
// Submit form
}
};
</script>
<template>
<VForm ref="form" @submit.prevent="handleSubmit">
<VTextField
v-model="email"
label="Email"
:rules="[rules.required(), rules.email()]"
/>
<VTextField
v-model="password"
label="Password"
type="password"
:rules="[rules.required(), rules.minLength(8)]"
/>
<VTextField
v-model="confirmPassword"
label="Confirm Password"
type="password"
:rules="[
rules.required(),
rules.match(password, 'Passwords must match')
]"
/>
<VBtn type="submit">Submit</VBtn>
</VForm>
</template>
Creating Custom Composables
Follow these patterns when creating new composables:
1. File Naming
Use use prefix: useFeatureName.ts
2. Export Pattern
export function useFeatureName() {
// Reactive state
const state = ref();
// Computed properties
const computed = computed(() => state.value);
// Methods
const method = () => {
// logic
};
// Return public API
return {
state,
computed,
method
};
}
3. TypeScript Types
export interface FeatureOptions {
option1?: string;
option2?: number;
}
export function useFeatureName(options: FeatureOptions = {}) {
// implementation
}
4. Add to Index
Update src/composables/index.ts:
export * from './useFeatureName';
Best Practices
- Keep composables focused: Each composable should have a single responsibility
- Use TypeScript: Define interfaces for options and return types
- Document parameters: Add JSDoc comments for better IDE support
- Test composables: Write unit tests for complex logic
- Avoid side effects: Keep composables pure when possible
- Export from index: Always export from
src/composables/index.ts