toRef, toRefs and unref
Keep a live reference to one property of a reactive object, destructure without losing reactivity, and accept refs or getters as arguments.
toRef(object, key, defaultValue?) | toRef(getter) | toRefs(object) | unref(val) | toValue(valOrRefOrGetter)Reading a property off a reactive object gives you its value, not a connection to it. These four utilities move between the two forms without breaking tracking.
Out of an object
toRef(object, key) returns a ref wired to that one property: writes to the ref reach the object, and writes to the object are visible through the ref. An optional third argument supplies a default when the property is undefined. toRefs(object) does the same for every enumerable property, so const { query, page } = toRefs(filters) gives refs that still track, where plain destructuring would have given dead copies.
Since Vue 3.3, toRef also accepts a getter: toRef(() => props.size) returns a readonly ref that follows the prop. This is the form to use for props, because the property version would let you write through to a prop and trigger the read-only warning.
Into a value
unref(x) is shorthand for the longhand isRef(x) ? x.value : x; the two are identical. toValue(x), added in 3.3, goes one step further and also calls x when it is a function, which is what lets a composable accept a plain value, a ref or a getter for the same argument.
Example
<script setup lang="ts">
import { reactive, toRefs, computed } from 'vue'
interface Filters {
query: string
page: number
}
const filters = reactive<Filters>({ query: '', page: 1 })
// One ref per property, each still wired to `filters`.
// Plain destructuring here would copy the values and stop tracking.
const { query, page } = toRefs(filters)
const label = computed<string>(
() => `page ${page.value} of "${query.value || 'everything'}"`
)
function nextPage(): void {
page.value += 1 // writes through to filters.page
}
function reset(): void {
filters.page = 1 // visible through the `page` ref
}
</script>
<template>
<input v-model="query" type="search" placeholder="Search" />
<p>{{ label }}</p>
<p>filters.page is {{ filters.page }}</p>
<button @click="nextPage">Next page</button>
<button @click="reset">Reset</button>
</template>
<style scoped>
button {
margin-right: 0.5rem;
}
</style>When to use it
- Destructuring a reactive options object without the values freezing
- Passing one property of a store or a prop into a composable that expects a ref
- Writing a composable argument that takes a constant, a ref or a getter
- Giving an optional property a default while keeping it writable
More examples
A composable that accepts a value, a ref or a getter
One signature covers three call styles, so callers never have to wrap a constant in a ref to satisfy your function. This is the standard argument shape for composables; every library composable you meet takes its inputs this way.
// src/composables/useGreeting.ts
import { computed, toValue, type ComputedRef, type Ref } from 'vue'
export function useGreeting(
name: string | Ref<string> | (() => string)
): ComputedRef<string> {
// toValue reads a ref's .value, calls a getter, or passes a plain value
// through. Added in Vue 3.3.
return computed(() => `Hello, ${toValue(name)}`)
}
// src/demo/greeting.ts
import { ref } from 'vue'
import { useGreeting } from '../composables/useGreeting'
const plain = useGreeting('Ada')
console.log(plain.value)
// Output: Hello, Ada
const who = ref('Ada')
const live = useGreeting(who)
who.value = 'Grace'
console.log(live.value)
// Output: Hello, GraceA readonly ref for a prop, and the longhand it replaces
Use the getter form whenever you need to hand a prop to something that expects a ref, such as a composable argument. Avoid toRef(props, 'size') for props: the ref it returns is writable, and writing it mutates a prop, which the development build warns about.
// src/components/SizeLabel.vue (script block only)
import { toRef, computed, type ComputedRef, type Ref } from 'vue'
const props = defineProps<{ size: number; unit?: string }>()
// Getter form, Vue 3.3+: a readonly ref that follows the prop.
const size: Readonly<Ref<number>> = toRef(() => props.size)
// The same thing written longhand.
const sizeLonghand: ComputedRef<number> = computed(() => props.size)
// Property form with a default, for a prop that may be undefined.
const unit = toRef(props, 'unit', 'px')
console.log(size.value, sizeLonghand.value, unit.value)
// Output: 10 10 px (when rendered as <size-label :size="10" />)Returning refs from a composable so callers can destructure
Internally the composable keeps one reactive object, which is comfortable to mutate. On the way out, toRefs converts it so `const { page } = usePagination()` behaves. Spread the result of toRefs when you also need to return functions alongside the state.
// src/composables/usePagination.ts
import { reactive, toRefs, type Ref } from 'vue'
interface Pagination {
page: number
perPage: number
}
export function usePagination(perPage = 20) {
const state = reactive<Pagination>({ page: 1, perPage })
function next(): void {
state.page += 1
}
// toRefs keeps the caller's destructuring reactive.
return { ...toRefs(state), next }
}
// src/demo/pagination.ts
import { usePagination } from '../composables/usePagination'
const { page, perPage, next } = usePagination(10)
next()
console.log(page.value, perPage.value)
// Output: 2 10
Discussion