
Vue 3 Composition API vs Options API: What Each One Is Actually For
- vuetelemetry
- Guides
- 6 min read
The Composition API is not a replacement for the Options API. They solve different problems at different scales, and the decision has less to do with which is newer and more to do with how your component grows. What each one does, where each one breaks down, and how to choose without rewriting working code.
Vue 3 ships both APIs. The Options API is what Vue has always had: a component is an object with `data`, `computed`, `methods`, `watch`, and lifecycle hooks as named properties. The Composition API, introduced in Vue 3, lets you write the same logic as plain functions inside a `setup()` function or a `<script setup>` block. Neither is deprecated, and neither is going away.
The Options API organises code by option type. All reactive state goes in `data()`. All computed values go in `computed`. All methods go in `methods`. This is clear for small components, because each concern is a few lines in each section and you can scan the sections quickly. It becomes a problem when a component grows, because a single feature (say, a search filter) has its state in `data`, its derived values in `computed`, its event handlers in `methods`, and its setup in `mounted`. The feature is scattered across four places in the file.
How the Options API organises code

The Composition API organises code by logical concern. Instead of putting all state in one place and all methods in another, you group the state, computed values, watchers, and functions that belong to a single feature together. A search filter becomes a block of 15 lines that contains everything it needs. A pagination feature becomes another block. They sit next to each other in the file, and you can read one without jumping to three other sections.
This only matters when components are large. A component with one piece of state, one computed value, and one method is equally readable in either API. The Composition API starts to pay off when a component has three or more independent concerns, because that is when the Options API forces you to interleave unrelated logic across sections.
How the Composition API organises code
`<script setup>` is the syntax most new Vue 3 code uses. It is a compile-time shorthand that removes the need to write an explicit `setup()` function and return statement. Variables declared at the top level are automatically available in the template. Props are declared with `defineProps()` and emits with `defineEmits()`. Both are compiler macros that do not need to be imported.
- The Options API organises code by type (data, computed, methods); the Composition API organises by feature
- Neither API is deprecated, and both run on the same reactivity system
- ref() is the recommended default over reactive() to avoid reassignment and destructuring bugs
- Composables replace mixins for cross-component logic reuse without name collisions
- TypeScript inference works naturally with the Composition API and struggles with Options API this-binding
- Migration is optional: both APIs coexist in the same project and even the same component
Reactivity in the Composition API uses `ref()` and `reactive()`. `ref()` wraps a single value and requires `.value` to read or write it in JavaScript (but not in the template, where Vue unwraps it automatically). `reactive()` wraps an object and its properties are reactive without `.value`. The most common source of bugs is forgetting `.value` in script code, or using it in the template where it is not needed.
ref, reactive, and script setup
`ref()` is almost always the right default. `reactive()` has two restrictions that catch people. First, you cannot reassign the whole object (because the proxy is on the original reference, not on the variable). Second, destructuring a reactive object loses reactivity on the destructured properties. `ref()` has neither problem, and the `.value` tax is minor compared to the debugging cost of those edge cases.
Composables are the main structural advantage. A composable is a function that uses Composition API primitives (`ref`, `computed`, `watch`, `onMounted`) and returns reactive state. `useMouse()`, `useFetch()`, `useLocalStorage()` are typical examples. You call them in `setup()` and get reactive values back. This is the pattern that the Options API cannot replicate cleanly: mixins share logic across components, but they merge properties by name, creating silent collisions and making it impossible to tell where a property comes from.
Mixins are the Options API equivalent, and they are problematic at scale. If two mixins define a `data` property with the same name, one silently wins. If a mixin adds a method, the component has no visible declaration for it. The source of a property becomes ambiguous as more mixins are added. The Vue documentation explicitly recommends composables over mixins for logic reuse in Vue 3.
Composables versus mixins
TypeScript support is significantly better in the Composition API. The Options API requires a `defineComponent()` wrapper and the type inference works through a complex `this` type that struggles with certain patterns (nested callbacks, external functions that reference component state). The Composition API uses plain variables with standard TypeScript types. `ref<number>(0)` gives you a `Ref<number>`. Composable functions have typed return values. IDE support (autocompletion, hover types, refactoring) works as it does with any TypeScript function.
Runtime performance is close, with two documented edges for the Composition API. The reactivity system is the same under both. The Vue documentation notes that code in `<script setup>` minifies better, because the template is compiled into a function in the same scope and local variable names can be shortened, while Options API code goes through property access on `this`. A project that uses only the Composition API can also switch the Options API off with the `__VUE_OPTIONS_API__` compile-time flag, which removes it from the production bundle. For most apps, these gains are small next to how the code is organised.
TypeScript, performance, and migration
Migration is not required. Vue 3 supports both APIs in the same project, in the same file if needed. An Options API component can use a composable by calling it in a `setup()` method added alongside the existing options. You do not need to rewrite working Options API components to use the Composition API. The practical migration path is to write new components with `<script setup>` and refactor old ones only when you are already editing them for other reasons.
When to choose the Options API. Small to medium components with straightforward logic. Teams where most developers are learning Vue and benefit from the explicit structure. Codebases with existing Options API code that does not need refactoring. Rapid prototyping where the overhead of naming refs and composables slows you down more than the organisational benefit helps.
When to choose the Composition API. Components with multiple independent concerns. Any situation where you need to share stateful logic across components (composables replace mixins). Projects with TypeScript. Libraries and open-source packages (composables are the expected distribution format). Large applications where component files regularly exceed 200 lines.
FAQ
Is the Options API deprecated in Vue 3?
No. The Vue team maintains both APIs as first-class citizens. The Options API is not deprecated, not planned for removal, and receives the same updates. The documentation presents both side by side. Choosing the Composition API is a decision about code organisation, not about staying current.
Should I rewrite my Options API components to use the Composition API?
Not unless you have a reason beyond the API itself. Working Options API code does not become worse because the Composition API exists. The practical approach is to write new components with the Composition API and refactor old ones only when you are already modifying them and the refactor makes the change easier to implement.
What is the difference between ref and reactive in Vue 3?
ref() wraps a single value and requires .value to access it in script code. reactive() wraps an object and its properties are reactive without .value. The key practical difference is that reactive() loses reactivity when you destructure it or reassign the whole object, while ref() does not have either problem. The Vue documentation and most community guides recommend ref() as the default.



**Runtime performance is close, with two documented edges for the Composition API.** The reactivity system is the same under both. The Vue documentation notes that code in `<script setup>` minifies better, because the template is compiled into a function in the same scope and local variable names can be shortened, while Options API code goes through property access on `this`. A project that uses only the Composition API can also switch the Options API off with the `__VUE_OPTIONS_API__` compile-time flag, which removes it from the production bundle. For most apps, these gains are small next to how the code is organised.