VVersions.dev

Vue Options API → Composition API

Moving from the Options API to the Composition API improves logic reuse and TypeScript inference. Both APIs coexist in Vue 3, so do it incrementally — component by component, with <script setup> and composables replacing mixins.

Pattern modernizationDifficulty: moderateEffort: Ongoing — incremental, component by componentlow risk

Last verified · Updated May 22, 2026

Converting Options API components to the Composition API with <script setup> is a low-risk, incremental modernization available once you are on Vue 3. Map data to ref/reactive, computed/watch to their function forms, and lifecycle methods to onMounted-style hooks. Both APIs coexist, so migrate one component at a time behind passing tests.

The incremental model

The Composition API does not replace the Options API — Vue 3 supports both, even within the same project. That means there is no cutover: you can convert a single component to <script setup>, extract shared logic into composables, and leave the rest on the Options API. Adopt it where logic reuse or TypeScript inference is painful, and leave simple components alone.

Transformation rules

Options APIComposition API
data() { return { count: 0 } }const count = ref(0)
nested/object statereactive({ ... })
computed: { double() {} }const double = computed(() => ...)
watch: { value() {} }watch(value, (next, prev) => ...)
mounted()onMounted(() => ...)
beforeUnmount()onBeforeUnmount(() => ...)
mixinscomposables (use* functions)

Component conversion

Options API vs <script setup>
<!-- Options API -->
<script>
export default {
  data() {
    return { count: 0 }
  },
  computed: {
    double() {
      return this.count * 2
    },
  },
  mounted() {
    console.log('mounted')
  },
}
</script>

<!-- Composition API with <script setup> -->
<script setup>
import { ref, computed, onMounted } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
onMounted(() => console.log('mounted'))
</script>

When the pattern applies

  • Components with logic you want to share across files (extract a composable instead of a mixin).
  • Components where TypeScript inference under the Options API is awkward.
  • Components you are already touching for the Vue 3 upgrade.

Composables vs mixins

  • Mixins merge implicitly and clash on naming; composables are explicit imports with clear return values.
  • A composable is a plain `use*()` function that calls ref/reactive/computed/watch and returns reactive state.
  • Prefer composables for new shared logic; convert mixins to composables as you touch the components that use them.
Migration executionAI-assisted conversion prompt
Convert this Vue Options API component to the Composition API using <script setup>. Map data to ref/reactive, computed/watch to their function forms, and lifecycle hooks (mounted to onMounted, beforeUnmount to onBeforeUnmount). Replace `this.` access with the local refs. If the component uses a mixin, extract the shared logic into a use* composable. Do not change the template or the component's public props/emits, and keep tests passing.

Safety: Convert one component at a time and keep the template and public API unchanged. Do not bundle unrelated refactors.

Edge cases

  • Reactivity loss: destructuring a reactive() object breaks reactivity — use toRefs.
  • `this` no longer exists in <script setup>; replace it with the local refs and helpers.
  • Two-way mixin state rarely maps cleanly — model it explicitly in the composable's return value.
  • Template refs and provide/inject replace patterns that previously reached through component internals.

Conversion checklist

  • data/computed/watch mapped to ref/reactive/computed/watch
  • Lifecycle methods mapped to on* hooks
  • Mixins extracted into composables where shared logic exists
  • Template and public props/emits unchanged
  • Component tests still pass after conversion

Official sources

  • Official docsVue.js Guidevuejs.orgreliability 98%

    Backs the breaking-change and migration-step claims.

  • Migration guideVue 3 Migration Guidevuejs.orgreliability 98%

    Backs the breaking-change and migration-step claims.

Copy-ready AI prompts

Structured prompts for an AI coding assistant. Inspect first, then execute incrementally, and keep a human in the review loop.

Repo inspectionRepo inspection prompt
You are helping with a Vue migration: Options API to Composition API modernization.

Do not edit files yet. First inspect the repository and report:
1. The exact vue version in package.json and the lockfile, plus vue-router, vuex, @vue/cli-service, and any vue plugins, and whether each has a Vue 3-compatible release.
2. The application entry point: `new Vue(...)` vs `createApp(...)`, and global registrations (Vue.use, Vue.component, Vue.directive, Vue.prototype / Vue.config.globalProperties).
3. Usage of removed Vue 2 features: filters, the global event bus / $on / $off / $once, `$children`, and `.sync` modifiers.
4. Components relying on multiple-root-node assumptions, v-model usage on custom components, and any `functional` components.
5. The build tool (Vue CLI vs Vite), test runner, and the build/lint/test commands.

Return: a migration risk summary, the highest-risk files, a suggested migration order, the commands to run before editing, and any questions that need human confirmation.

Safety: Inspection only. The agent must not modify files in this step.

Works with Claude Code, Cursor, GitHub Copilot

Migration executionComposable extraction prompt
Refactor shared logic out of this Vue mixin into a Composition API composable. Create a `use*` function that calls ref/reactive/computed/watch and returns the reactive state and methods the components need. Update each component that used the mixin to call the composable from <script setup> instead. Preserve behavior and keep tests passing; do not change templates or public component APIs.

Safety: One mixin at a time. Preserve behavior exactly and keep templates and public APIs unchanged.

Works with Claude Code, Cursor, GitHub Copilot

Test plan

Commands

  • npm run lint
  • npm test -- --watch=false
  • npm run build

Manual checks

  • Render: confirm each converted component renders identically to its Options API version.
  • Reactivity: verify computed values and watchers still fire after conversion.
  • Composables: confirm extracted composables return reactive state (no lost reactivity from destructuring).

Regression risks

  • Lost reactivity from destructuring a reactive() object without toRefs.
  • Mixin-to-composable extraction subtly changing initialization order.
  • `this`-based access left behind after moving to <script setup>.

Acceptance criteria

  • Each converted component renders and behaves identically to before.
  • Shared logic lives in composables, not mixins.
  • Lint, tests, and build pass with no template or public-API changes.