shallowRef can be used to optimize the reactivity of your Vue application. As you might have guessed, shallowRef is a shallow version of ref().
Let's start by defining a counter state using shallowRef:
<script setup lang="ts">
import { shallowRef } from 'vue'
const state = shallowRef({
count: 0,
})
</script>
<template>
<span>Count: {{ state.count }}</span>
</template>
Now let's try to modify the count value:
<script setup lang="ts">
import { shallowRef, watch } from 'vue'
const state = shallowRef({
count: 0,
})
const increment = () => {
state.value.count = 2 // ⚠️ doesn't trigger change (watcher & UI update)
}
watch(state, (newState) => {
console.log('new state', newState)
})
</script>
<template>
<span>Count: {{ state.count }}</span>
<button @click="increment">Increment count</button>
</template>
Unlike ref(), the inner value of a shallow ref is stored and exposed as-is, and will not be made deeply reactive. Only the .value access is reactive.
The reactivity is correctly triggered if we pass a complete new value to the .value property:
<script setup lang="ts">
import { shallowRef, watch } from 'vue'
const state = shallowRef({
count: 0,
})
const setNewValue = () => {
state.value = { count: 2 } // triggers change
}
watch(state, (newState) => {
console.log('new state', newState)
})
</script>
<template>
<span>Count: {{ state.count }}</span>
<button @click="setNewValue">Set new .value</button>
</template>
shallowRef() is typically used for performance optimizations of large data structures, or integration with external state management systems.
If you liked this Vue tip, follow me on X to get notified about new tips, blog posts, and more.
Vue Tip: Declare and Mutate v-model Props as Normal Variable Using defineModel
Nuxt Tip: Render Component Only on Client-Side

