The reactive function
reactive is the second way to declare state. It makes the object itself reactive, so its fields are accessed directly, without .value. It suits a group of related values, such as filter settings.
Declaring
<!-- src/App.vue -->
<script setup>
import { reactive } from 'vue'
const filter = reactive({
query: '',
onlyActive: false,
})
function toggle() {
filter.onlyActive = !filter.onlyActive
}
</script>
<template>
<button @click="toggle">
{{ filter.onlyActive ? 'Show all' : 'Only active' }}
</button>
</template>Every click changes the button label. filter.onlyActive is changed in the script without .value, and Vue still re-renders the template.
Limits
reactive works only with objects, arrays, Map and Set. You cannot pass a string or a number — those need ref.
The second limit matters more: reactivity is lost if you replace the whole object or take it apart with destructuring.
<script setup>
import { reactive } from 'vue'
let filter = reactive({ query: '', onlyActive: false })
// The link is lost: the template still sees the old object
filter = reactive({ query: 'bread', onlyActive: true })
// query is a plain string, Vue will not see its changes
const { query } = filter
</script>Destructuring from the tutorial's objects lesson copies the values into plain variables here, and they stop being reactive.
Comparison
ref | reactive | |
|---|---|---|
| Types | Any | Objects and collections only |
| Access in script | count.value | filter.query |
| Replacing entirely | todos.value = [] | Not possible |
| Destructuring | Not needed | Breaks reactivity |
Which to choose
The Vue documentation recommends ref by default: it works with any value, it can be replaced entirely, for example with data from an API, and .value in the code shows at once where a reactive variable is. The rest of the course uses ref. reactive is handy for small grouped objects such as a form that is never replaced as a whole.
<script setup>
import { ref } from 'vue'
const filter = ref({ query: '', onlyActive: false })
function reset() {
filter.value = { query: '', onlyActive: false }
}
</script>With ref, resetting to the initial values is a single assignment.
Common mistake
Loading data and assigning it to a reactive variable:
<script setup>
import { reactive } from 'vue'
let todos = reactive([])
async function load() {
const response = await fetch('/todos.json')
todos = await response.json() // The list on the page does not update
}
</script>Declare the list with ref([]) and write todos.value = await response.json() — then the new array reference is noticed.