Composition API Vue 3
Replacing the Options API with the Composition API.

I am a Frontend Developer and Technical Writer. I love helping other developers understand difficult concepts through writing. If you have any questions regarding tech, or you just want to connect, message me on any of my socials, be sure I will get back to you as soon as possible.
In Vue 3, there are two ways we can write our component. The first way is with the Options API, a set of options like the data, computed properties, methods, watchers, and lifecycle hooks.
Another way we can write our component is by using the Composition API, which was introduced in Vue 3. The Composition API allows us to write our component logic using a set of imported functions, like ref, reactive, watch, lifecycle hooks, etc.
In this guide, we'll be diving deep into the Composition API, exploring the most important features. If you're reading this, chances are that you've already learned and used the Options API, so, for you to better understand the Composition, we'll be replacing each of the Options API features with its Composition API equivalent. With that said, if you're ready, go grab your popcorn and let's roll.
Prerequisites
Having experience with Options API before reading this guide is not compulsory but necessary, since what we'll be doing essentially in this guide is replacing the Options API with the Composition API, so it is important that you already have a taste of the Options API before reading this guide, so that you will have a good understanding of what we are doing.
Other than that, just have Node.js v16+ and vs code or any other text editor installed on your machine and you're good to go.
Setting up our coding environment
Open your machine and run the following command: npm init vue@latest. This will scaffold a new Vue project for you by prompting you to answer a series of questions. Use the up arrow and down arrow keys to select from the options provided. Be sure to choose no if you are not sure of the option.
Ways we can write the composition API
Before we start writing codes, I want to first mention that there are two ways we can write the Composition API. The first way is with the setup() method and the other way is by using the <script setup></script>. Let's see an example of how to use both.
setup() Example
<script>
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(1)
const doubleCount = computed(() => {
return count.Value += 1
})
return {
count,
doubleCount
}
}
}
</script>
<template>
<div>{{ count }}</div>
<div>{{ doubleCount }}</div>
</template>
You can see how we are writing all of our component logic inside setup() and returning them. Just for clarity, the count variable will output 1 and doubleCount will output 2. Don't worry if you don't understand what ref and computed are, we'll explain everything in detail soon.
NOTE: We can combine both the Options API and the setup() method in the same file.
<script setup> Example
Another way we can write our component logic in the composition API is by writing all of our logic inside <script setup> </script>. Let's see an example:
<script setup>
import { ref, computed } from 'vue'
const count = ref(1)
const doubleCount = computed(()
return count.Value += 1
})
</script>
<template>
<div>{{ count }}</div>
<div>{{ doubleCount }}</div>
</template>
This is the same thing we did inside the setup() method, but this time we're using the <script setup>. You can choose between any of the two styles, it all depends on your preference. Throughout this guide, we'll be using both the setup() and the <script setup> style to write our logic.
Replacing data
In this section, we'll see how to replace the data option with composition API, we'll be doing this with the ref function. Let's see examples of how we can achieve this.
<template>
<div>
<h1>Options API: {{ oName }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
oName: 'Agu Wisdom'
}
}
}
</script>
In the above code, we have defined a oName variable within the Options API data block, and we are rendering to the browser in our template block.
Let us now see how we can replace the data with the Composition API
<template>
<div>
<h1>Options API: {{ oName }}</h1>
<h1>Composition API: {{ cName }}</h1>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup () {
const cName = ref('Agu Wisdom')
return {
cName
}
},
data() {
return {
oName: 'Agu Wisdom'
}
}
}
</script>
Notice how we are declaring a new variable in the setup block and assign it the ref() function, and exposing it to our template using the ES6 shorthand.
The ref() is used to create a reactive reference to a value. we can access the ref() using .value. Let's say we want to log the cName variable to the console, we can do that like so; console.log(cName.value). We can also modify a ref value like so; cName.value = "Wisdom". Note that we cannot assess cName outside the setup block.
Note: The ref() function is different from the ref attribute. The ref attribute is a special built-in attribute that is used to create a reference to an element or a child component within a Vue component. It allows us to access and manipulate the underlying DOM element or component instance directly. more on the ref attribute. Optionally, we can use reactive() instead of ref(). Learn more about reactive() here
Replacing Computed Properties
In Vue.js, computed properties are used to perform basic computations. Below is a simple example from Vue Options API documentation on how to use computed properties:
export default {
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
},
computed: {
// a computed getter
publishedBooksMessage() {
// `this` points to the component instance
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
}
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
In the computed property block, we're telling Vue to display Yes in the browser if the length of the book is greater than 0 or No if otherwise.
Now, let's see how we can also achieve the same result we the Composition API:
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// a computed ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
With the Composition API, we use computed() functions instead of computed properties. The computed() function returns a computed ref which we can then use in our template.
Also, notice how we are using the reactive() function instead of ref(). This shows that we can optionally use reactive instead of ref, with little differences in how they work.
Replacing Methods Properties
data() {
return {
name: 'Vue.js'
}
},
methods: {
sayHello() {
alert(`Hello ${this.name}!`)
}
}
<button @click="sayHello">Greet</button>
In Vue, a methods property is simply a logic defined to be called in response to events or actions. As you can see from the example, we have declared a method function sayHello inside the methods property and sayHello function alerts Hello Vue.js anytime it is called.
Let's see how we can do the same with the Composition API:
const name = ref('Vue.js')
function sayHello() {
alert(`Hello ${name.value}!`)
}
<button @click="sayHello">Greet</button>
Perhaps, this is the simplest replacement for any of the options in the Options API. We just write our logic inside a function block and we're ready to use it in our template. Is that not sweet?
Replacing Watchers
The watchers are used to trigger the DOM whenever a reactive property changes. Below is an example of how the watch option is used:
<script>
export default {
data() {
return {
count: 0
};
},
watch: {
count(newValue, oldValue) {
console.log(`Count changed from ${oldValue} to ${newValue}`);
}
},
methods: {
increment() {
this.count++;
}
}
};
</script>
<template>
<div>
<p>Current count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
The watch option allows us to watch for changes in a particular data property or computed property and performs actions when those changes occur.
With the Composition API, we can replace the watch option with a special watch() function.
import { ref } from 'vue'
const count = ref(0)
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`
})
function increment() {
this.count++;
}
</script>
<template>
<div>
<p>Current count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
The watch() function takes two arguments, the first argument is the value to watch, and the second argument is the function to run when the value changes.
The first argument can be a reactive ref, computed ref, a reactive object, or an array of multiple values, as long as the values are reactive.
We also have the watchEffect(), which can be used to watch multiple values in a nested data structure. find out more about watchEffect() here
Replacing Lifecycle Hooks
This one is relatively simple, aside from the remover of the created() Lifecycle from the Composition API, everything is pretty much the same with a little change in the naming convention. For example, we use the mounted lifecycle in the Options API but in the Composition API, it is changed to onMounted they both work the same, just a little change in the naming convention.
Wrapping up
The Composition API has provided ways to write cleaner and less redundant codes in Vue.js, it might seem confusing at the start, especially with understanding how reactivity works in Vue 3, but trust me, the more you write your codes and logic with the Composition API, the more you'll understand it.
