Vue 3's reactivity system is built around two core functions: ref() and reactive(). Both create reactive state, but they behave differently.
import { ref } from 'vue'
const count = ref(0)
const name = ref('Alice')
const isOpen = ref(false)
const items = ref([]) // ref can also hold arrays/objects
// Access / mutate via .value in script
count.value++
name.value = 'Bob'
items.value.push('new item')
// In templates, .value is unwrapped automatically
<template>
<p>{{ count }}</p> <!-- no .value needed -->
<button @click="count++">+</button>
</template>
import { reactive } from 'vue'
const user = reactive({
name: 'Alice',
age: 30,
address: { city: 'Paris' }
})
// Access directly — no .value
user.name = 'Bob'
user.address.city = 'Lyon'
ref() for primitives (string, number, boolean) and when you need to reassign the whole valuereactive() for objects where you only ever mutate nested propertiesref() for everything for consistency