toRef, toRefs & unref

Convert Between ref and reactive Properties

toRef, toRefs & unref

These helpers preserve reactivity when destructuring or passing pieces of a reactive object — and let composables accept either a ref or a plain value.

5 min read Level 2/5 #vue#refs#reactive
What you'll learn
  • Use toRef to make a single ref from a reactive property
  • Use toRefs to destructure without losing reactivity
  • Use unref to read a possibly-ref value

Reactive objects lose reactivity when you destructure or pass a property by value. The trio toRef, toRefs, and unref lets you bridge between the two reactive worlds.

toRef — One Property at a Time

toRef(state, 'key') returns a ref that stays in sync with state.key.

import { reactive, toRef } from 'vue'

const state = reactive({ name: 'Ada', age: 36 })
const nameRef = toRef(state, 'name')

console.log(nameRef.value) // 'Ada'
state.name = 'Bob'
console.log(nameRef.value) // 'Bob'

toRefs — Destructure Everything

toRefs(state) returns an object whose properties are all refs.

import { reactive, toRefs } from 'vue'

const state = reactive({ count: 0, name: 'Ada' })
const { count, name } = toRefs(state)

count.value++          // updates state.count
console.log(state)     // { count: 1, name: 'Ada' }

This is the standard pattern when a composable returns a reactive object.

function useUser() {
  const state = reactive({ user: null, loading: false })
  return toRefs(state)
}

// Caller can destructure freely
const { user, loading } = useUser()

unref — Read Either

unref(x) returns x.value if x is a ref, otherwise x. Use it when accepting either form.

import { unref, ref } from 'vue'

function log(input: number | { value: number }) {
  console.log(unref(input))
}

log(5)           // 5
log(ref(5))      // 5

For reactive tracking, prefer toValue — it accepts refs, getters, or plain values.

import { toValue, type MaybeRefOrGetter } from 'vue'

function double(x: MaybeRefOrGetter<number>) {
  return toValue(x) * 2
}

double(3)              // 6
double(ref(3))         // 6
double(() => count.value) // 6 — getter form

This pattern is how modern composables (VueUse, etc.) accept “ref or value” inputs gracefully.

Conditional Rendering — v-if →