Vue 3 Composition API: Why and When
I discuss what the Composition API brings over Options API in Vue 3, and when you should prefer it, with concrete examples.
I had been following the Vue 3 beta from a distance. When I saw the Composition API announcement, my first reaction was skepticism: is this just React Hooks for Vue? Why did they feel the need for this change?
Answering that question with real code turned out to be more useful than theorizing. A few weeks back I used the Vue 3 beta on a project. I ran both the new API and the old Options API side by side in the same codebase. Once I saw the difference firsthand, the answer to “why” became a lot clearer.
Why Options API Has Its Limits
Options API is Vue 2’s familiar face: data, computed, methods, watch — everything has its category. That structure is intuitive for small components. The problem surfaces when a component grows: logic belonging to a single feature gets scattered across four different blocks.
Consider a user search feature. The search query lives in data, the filtering logic in computed, the debounce timer back in data, and the reset action in methods. All four pieces belong to the same feature, yet they end up in four different places in the file.
// Options API — search logic scattered across four sections
export default {
data() {
return {
query: '',
timer: null,
results: [],
};
},
computed: {
filteredResults() {
return this.results.filter(r => r.active);
}
},
methods: {
onInput(value) {
clearTimeout(this.timer);
this.timer = setTimeout(() => this.search(value), 300);
},
async search(query) {
const { data } = await axios.get('/api/search', { params: { q: query } });
this.results = data;
},
reset() {
this.query = '';
this.results = [];
}
}
};
The Same Logic with Composition API
With Composition API you can keep that same logic together in a single setup function — or better yet, in a dedicated composable you can extract:
// useSearch.js — as a standalone composable
import { ref, computed } from 'vue';
import axios from 'axios';
export function useSearch() {
const query = ref('');
const results = ref([]);
let timer = null;
const filteredResults = computed(() =>
results.value.filter(r => r.active)
);
function onInput(value) {
clearTimeout(timer);
timer = setTimeout(() => search(value), 300);
}
async function search(q) {
const { data } = await axios.get('/api/search', { params: { q } });
results.value = data;
}
function reset() {
query.value = '';
results.value = [];
}
return { query, filteredResults, onInput, reset };
}
Using it in a component:
import { useSearch } from '@/composables/useSearch';
export default {
setup() {
const { query, filteredResults, onInput, reset } = useSearch();
return { query, filteredResults, onInput, reset };
}
};
The search logic now lives in a single file, is portable, and is independently testable. When another component needs the same behavior, you just import useSearch.
Comparison with Mixins
In Vue 2, mixins filled the role that composables play today. But mixins have well-known problems: if two mixins define a property with the same name, it’s unclear which one wins. You look at this.results and wonder — did the component define that, or did one of the mixins inject it? With composables, that ambiguity is gone. You name what useSearch returns yourself, and where it comes from is explicit.
This distinction seems trivial in small components. In a large component where four or five mixins are mixed together, that ambiguity becomes a real maintenance burden.
When Composition API, When Options API?
Let me answer this directly:
For small, self-contained components, Options API is still perfectly readable. A few data fields, a few methods — the categorization is clarity, not overhead. Especially in Vue 2 codebases, migrating to Composition API unnecessarily just makes the code more complex.
For reusable logic, Composition API is a clear win. What you used to do with mixins in Vue 2 becomes far cleaner with composables — no name collisions, no properties of uncertain origin.
For large components, Composition API lets you group logic by feature rather than by type. That is a significant difference.
TypeScript compatibility is another compelling reason. Composition API works much better with TypeScript; type inference in Options API is limited by comparison.
Migrating from Vue 2
Options API is not being removed in Vue 3 beta — both APIs can be used together. You don’t have to migrate existing Vue 2 projects all at once. Using Composition API for new components while keeping Options API for existing ones is a sensible interim strategy.
Dismissing Composition API as “a feature added under React’s influence” is unfair. It solves a real problem — logic scattered throughout a component — in a concrete and practical way. For someone with Vue 2 habits, picking up this API takes a few days; over the long haul, that investment pays off the moment you move beyond trivial components.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.