From mixins to composable functions
In earlier versions of Vue, sharing stateful logic between components often meant relying on mixins. Mixins suffered from namespace collisions, implicit dependencies, and made tracing the origin of properties frustrating in large codebases.
The Composition API introduced composables—plain JavaScript/TypeScript functions that leverage Vue’s reactivity system. They provide clear inputs, explicit return values, and full TypeScript autocomplete without any magic.
Designing composables as small APIs
A great composable should feel like a dedicated micro-library. Instead of dumping arbitrary reactive refs into a single file, define clear contracts. Accept refs or getters for parameters that might change over time using `toValue()` or `maybeRef()`. Return readonly states when consumers should not mutate data directly.
By keeping composables focused on a single concern—such as managing local storage persistence, handling keyboard shortcuts, or tracking network status—they become effortless to test and reuse across multiple projects.
Composables vs global stores
Not every piece of shared state needs to live in a global store like Pinia. For component-level state that should be destroyed when the component unmounts, or for reusable UI behavior, composables provide all the benefits of reactivity with zero global state bloat.