Vue Composition API Introduction
The Composition API is Vue 3's function-based way of writing components, letting you group related logic together instead of splitting it across data, methods, and computed.
Why a New API?
In the Options API, a single feature -- say, a search box with debouncing -- often has its state in data, its logic in methods, and its derived values in computed, spread across the file in whatever order those options happen to be declared. As a component grows, related code drifts apart. The Composition API instead lets you write all the reactive state and logic for one feature together as plain JavaScript, in whatever grouping makes sense, and extract it into a reusable function -- a composable -- when another component needs the same behavior.
setup() and <script setup>
The Composition API's entry point is a setup() function that runs once, before the component is mounted, and returns the values and functions the template should have access to. <script setup> is compiler-time sugar for this: everything you declare at the top level of a <script setup> block -- variables, functions, imports -- is automatically exposed to the template, so you never write an explicit return statement.
A Component Written with <script setup>
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
</script>
<template>
<p>Count: {{ count }} (doubled: {{ doubled }})</p>
<button @click='increment'>Add one</button>
</template>ref vs reactive
ref() wraps any value -- a number, a string, an object, an array -- in a reactive container with a single .value property; you read and write that property in script code, but Vue automatically unwraps it in the template, so you write count rather than count.value there. reactive() instead wraps an object directly, making its properties reactive without a .value indirection, but only for objects: reactive(0) doesn't work, and reassigning the whole object with = replaces the reactive proxy, breaking the connection. As a rule of thumb, use ref for standalone values and reactive for grouped, related state like a form.
Choosing Between ref and reactive
<script setup>
import { ref, reactive } from 'vue'
// A single value: ref
const submitCount = ref(0)
// A group of related fields: reactive
const form = reactive({
email: '',
password: ''
})
function submit() {
submitCount.value++
console.log('Submitting', form.email)
}
</script>
<template>
<input v-model='form.email' placeholder='Email' />
<input v-model='form.password' type='password' placeholder='Password' />
<button @click='submit'>Log in ({{ submitCount }})</button>
</template>The Same Component, the Options Way
Seeing the two styles side by side makes the trade-off concrete. Below is a counter component written with the Options API -- notice how count, doubled, and increment are declared in three separate options, even though they describe one small piece of behavior.
The Options API Equivalent
<script>
export default {
data() {
return { count: 0 }
},
computed: {
doubled() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
<template>
<p>Count: {{ count }} (doubled: {{ doubled }})</p>
<button @click='increment'>Add one</button>
</template>Mapping Concepts Between the Two APIs
Exercise: Vue Composition API
What is a key difference between ref() and reactive() for primitive values?