Skip to main content

Vue Options API vs Composition API (Implementation Guide)

Purpose​

This guide is for developers working on Vue projects that use a mix of Options API and Composition API. It outlines key implementation differences, and when/how to use one over the other in real-world situations.


1. Component Structure​

Options API​

export default {
data() {
return {
count: 0,
};
},
computed: {
double() {
return this.count * 2;
},
},
methods: {
increment() {
this.count++;
},
},
};

Composition API (with <script setup>)​

<script setup>
import { ref, computed } from "vue";

const count = ref(0);
const double = computed(() => count.value * 2);
function increment() {
count.value++;
}
</script>

2. Props and Emits​

Options API​

export default {
props: ["label"],
emits: ["clicked"],
};

Composition API​

<script setup>
const props = defineProps(['label']); const emit = defineEmits(['clicked']);
</script>

3. Refs and Template Access​

Options API​

this.$refs.myInput.focus();

Composition API​

const myInput = ref(null);
myInput.value?.focus();

Note: In <script setup>, template refs are just local variables.

Think of ref="..." as similar to document.querySelector() β€” you're creating a reference to a specific DOM or component instance. However, Composition API ref() is also used to create reactive primitives that aren’t tied to DOM β€” like const count = ref(0).


4. Exposing Public Methods to Parent​

Options API​

All methods are exposed automatically via this.$refs.childComponent.

Composition API​

Use defineExpose():

const reset = () => {
/* ... */
};
defineExpose({ reset });

Then in parent:

const comp = ref(null);
comp.value?.reset();

Without defineExpose, ref="childComponent" will return an empty object {}.


5. Lifecycle Hooks​

Options API​

created() {
console.log('Component created');
}

Composition API​

import { onMounted } from "vue";
onMounted(() => {
console.log("Component mounted");
});

Note: created() has no direct equivalent. Just run code at top-level of <script setup>.


6. State Management (e.g., Pinia)​

Both APIs use stores similarly, but Composition API allows cleaner integration:

const store = useMyStore();
store.someAction();

In Options API, you usually assign the store in data() or created().


7. computed vs watch​

  • Use computed when you want a derived value.
  • Use watch when you want to run side effects on change.

Options API​

watch: {
someProp(val) {
this.doSomething(val);
}
}

Composition API​

watch(
() => props.someProp,
(val) => {
doSomething(val);
}
);

8. Summary Table​

FeatureOptions APIComposition API
Statedata()ref(), reactive()
Computedcomputed:computed()
Watcherswatch:watch()
Methodsmethods:regular functions
Lifecyclemounted()onMounted() etc.
Props/Emitprops, emitsdefineProps(), defineEmits()
Template Refsthis.$refs.xref(null) + x.value
Public Methodsauto-exposeddefineExpose()

Recommendation​

  • Use Composition API for new components.
  • Stick with Options API in legacy components unless you're refactoring.
  • When mixing, keep each component in one style β€” don't hybridize unless absolutely necessary.

Additional Tips​

  • Script Setup runs like created(), so you don’t need a lifecycle method to fetch data or set defaults.
  • Avoid using ref(store.something) if you want reactivity β€” prefer computed(() => store.something) or use an empty ref then populate it with a watch on the store item.
  • Prefer defineExpose() only when you need parent control over a child component.
  • Use ref="child" + ref(null) + defineExpose() in child if you want the parent to call methods like .reset() or .focus() on the child.