A Practical Guide to ref and reactive in Vue 3

Navigating the world of Vue 3 often brings developers to a critical crossroads: when should you use ref()
, and when is reactive()
the right choice? š¤ Both are powerful tools in the Composition API for creating reactive state, but they have key differences that can significantly impact your code's clarity and maintainability.
Let's dive deep, demystify these two functions, and establish clear guidelines to help you write cleaner, more efficient Vue 3 applications.
Before we compare ref
and reactive
, let's quickly recap what reactivity means in Vue. At its core, reactivity is the mechanism that allows Vue to automatically track changes to your data and update the DOM whenever that data changes.
In the Composition API, we explicitly declare reactive state using functions like ref()
and reactive()
.
ref()
š¦Think of ref()
as a "reference" that can hold any value type ā primitives like strings
and numbers
, or complex types like objects
and arrays
. It wraps the inner value in a special object with a .value
property.
import { ref } from "vue";
// For primitive types
const count = ref(0);
const frameworkName = ref("Vue");
const isActive = ref(true);
// For object types
const user = ref({
id: 1,
name: "Alex",
});
ref
To access or modify the value of a ref
, you must use the .value
property inside your <script setup>
block.
// Accessing the value
console.log(count.value); // Output: 0
// Modifying the value
count.value++;
console.log(count.value); // Output: 1
// Reassigning the entire object
user.value = { id: 2, name: "Beth" };
⨠Magic Unwrapping: One of the best features of ref
is that when used in a template (<template>
), Vue automatically "unwraps" it for you. This means you don't need to use .value
in your HTML
<template>
<p>Count is: {{ count }}</p>
</template>
ref()
string
, number
, boolean
, null
, undefined
. This is the primary use case for ref
. ref
allows you to completely replace the value it holds (e.g., myRef.value = newValue
), which is something you can't do with reactive
. For more details, check out the official Vue ref
documentation.
reactive()
šThe reactive()
function is used exclusively for object types, such as objects
, arrays
, Map
, and Set
. It returns a reactive proxy of the object
itself. You don't get a wrapper object
with a .value
property.
import { reactive } from "vue";
const state = reactive({
counter: 0,
user: {
name: "Chris",
loggedIn: false,
},
items: ["Item 1", "Item 2"],
});
reactive
PropertiesYou access and modify properties directly on the reactive object, just like a regular JavaScript object.
// Accessing properties
console.log(state.user.name); // Output: 'Chris'
// Modifying properties
state.counter++;
state.user.loggedIn = true;
state.items.push("Item 3");
reactive()
A key feature of reactive
is that it provides deep reactivity. This means any nested objects or arrays within the reactive
object are also automatically wrapped in reactive proxies. Changes to these nested properties will be tracked.
reactive
Pitfall āThere's one major rule with reactive
: you cannot reassign the entire object. Doing so will break the reactive connection, and your component will stop tracking changes.
let state = reactive({ count: 0 });
// THIS WILL BREAK REACTIVITY!
// The 'state' variable is now just a plain, non-reactive object.
state = { count: 1 };
Similarly, you lose reactivity if you destructure properties from a reactive
object. To work around this, you can use helpers like toRefs
.
For more details, see the official Vue reactive
documentation.
ref
vs. reactive
: The Key Differences SummarizedFeature | ref() | reactive() |
---|---|---|
Data Types | Any value (primitives, objects, arrays) | Objects, arrays, Maps, Sets only |
Access in Script | Via the .value property | Directly on the object |
Access in Template | Automatically unwrapped (no .value ) | Directly on the object |
Reassignment | ā
Safe to reassign the entire ref (myRef.value = ... ) | ā Cannot reassign the entire object |
Under the Hood | Wraps value in an object. Uses reactive internally for objects. | Returns a reactive proxy of the object. |
Here's a simple and effective heuristic followed by many in the Vue community:
ref()
for all reactive variables. It works for everything and is consistent. Using .value
everywhere in your script makes it clear you're dealing with a reactive variable. reactive()
when you have a complex object with multiple properties that you want to group together. Think of it as a single "state" object for your component. This can make your code cleaner by grouping related data. Many teams default to using ref()
for consistency and only use reactive()
for large, grouped state objects. There's no single "right" answer, but this approach avoids the common pitfalls of reactive
.
Ultimately, the best choice depends on your specific needs and team conventions. The most important thing is to be consistent
Happy coding š»