Appearance
State Management with Composables
What are Composables?
Composables are reusable functions that leverage Vue's Composition API to encapsulate and reuse stateful logic. In the context of state management, composables provide a lightweight, flexible approach to sharing reactive state and logic across components without the complexity of a full state management library.
Note
Composables also serve a purpose to extract and encapsulate complex logic, making it easier to manage, re-use and test. This is still a main purpose it fulfills. Using composables for state management should be clearly distinguishable from logical reusable composables.
Key Characteristics
- Reactive State: Built on Vue's reactivity system (
ref
,reactive
,computed
) - Reusable Logic: Encapsulate state and related operations in functions
- Composition: Can be combined and composed together
- Type Safety: Full TypeScript support with excellent inference
- Lightweight: No additional dependencies or setup required
When to Use Composables for State Management
Ideal Use Cases
- Shared Component State: When multiple components need access to the same reactive data
- Form State Management: Managing complex form state, validation, and submission logic
- Local Feature State: State that's specific to a particular feature or page (but spans multiple components)
- Simple Global State: Lightweight alternative to Pinia for simple shared state
When to Choose Composables Over Pinia
- Simple State: When your state management needs are straightforward
- Feature-Specific: State that's only relevant to a specific feature or module
- Prototype/MVP: Quick development without store setup overhead
- Component Libraries: Creating reusable component logic
- Gradual Migration: Transitioning from Options API or adding reactivity incrementally
Best Practices for State Management
1. State Isolation
ts
import { ref } from 'vue'
// State isolation on instance level
export function useUserState() {
const user = ref(null) // <- place state variables inside the composable
// ... logic
return { user, ... }
}
ts
import { ref, readonly } from 'vue'
// Global shared state
const globalUser = ref(null) // <- place state variables at module scope, outside the composable
export function useGlobalUser() {
// ... logic
return { user: readonly(globalUser), ... }
}
2. Readonly Exports
Always export state as readonly to prevent accidental mutations:
ts
export function useStore() {
const state = ref(0);
return {
state: readonly(state), // Prevent direct mutation
setState: (value) => (state.value = value),
};
}
References
Official Vue Documentation
Advanced Patterns
Community Resources
- VueUse - Collection of essential Vue Composition Utilities
- Vue Composition API RFC