Computed Properties

This section documents the current Vue 3.x release line as published at the official Vue.js documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. The Composition API with <script setup> is the authoring style used throughout; the Options API is shown only as a contrast.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since Vue and its ecosystem iterate quickly.

This section’s bibliography lists the reference material consulted while preparing these pages.

A computed property is a reactive value derived from other reactive state. You give computed() a getter; it returns a ref whose .value is the getter’s result, recomputed only when a dependency changes. This page follows Computed Properties and the computed() API entry.

The basics

<script setup>
import { ref, computed } from 'vue'

const first = ref('Ada')
const last = ref('Lovelace')

const fullName = computed(() => `${first.value} ${last.value}`)
</script>

<template>
  <p>{{ fullName }}</p>   <!-- auto-unwrapped, like any ref -->
</template>

In the template fullName behaves like the other refs. In script you read fullName.value.

Caching vs. a method

A computed property caches. Its getter re-runs only when one of the reactive values it read last time changes; any number of reads in between return the stored result. A method, by contrast, runs on every re-render, because the template has no way to know whether its result would differ.

<script setup>
import { ref, computed } from 'vue'

const list = ref([1, 2, 3, 4, 5, 6])

// cached: recomputed only when `list` changes
const evens = computed(() => list.value.filter((n) => n % 2 === 0))

// not cached: runs on every render that calls it
function evensNow() {
  return list.value.filter((n) => n % 2 === 0)
}
</script>

For an expensive derivation — filtering a large array, formatting dates, walking a tree — the cache is the whole point. computed also does not recompute when an unrelated piece of state triggers the re-render.

When a method is the right choice

Use a method (or a plain function) instead of computed when:

  • the result takes arguments — formatPrice(item) in a v-for; a computed takes none;

  • you want it to run every time on purpose, e.g. Date.now() or Math.random(), which have no reactive dependency to cache against;

  • it performs an action rather than producing a value — see Reactivity Fundamentals and watch for side effects.

Writable computed

Pass an object with get and set to make a computed assignable. The setter runs when code writes .value, and typically writes back to the underlying refs.

<script setup>
import { ref, computed } from 'vue'

const first = ref('Ada')
const last = ref('Lovelace')

const fullName = computed({
  get() {
    return `${first.value} ${last.value}`
  },
  set(value) {
    [first.value, last.value] = value.split(' ')
  },
})

fullName.value = 'Grace Hopper'   // runs set(); first -> 'Grace', last -> 'Hopper'
</script>

Keep getters pure

A getter must only compute and return. Do not mutate other reactive state, perform async requests, or touch the DOM inside it — those make the value unpredictable and can loop. Put effects in watch or watchEffect. It is also fine to return a fresh object or array from a getter; since 3.4 Vue compares the getter’s result and skips downstream updates when it is deeply unchanged from the previous run, so a stable value does not cause needless re-renders.

computed vs. watch

Both react to state, but they answer different questions:

Use computed when Use watch / watchEffect when

you need a value that is a pure function of other state

you need a side effect — fetch data, write to localStorage, start a timer — in response to a change

the template or other logic reads the result

nothing reads a result; you are reacting, not deriving

Reaching for watch to copy one ref into another is almost always a computed in disguise. See Reactivity Fundamentals for watch.

Debugging a computed

For development, computed() accepts onTrack and onTrigger hooks as a second (or, with the object form, extra) argument. onTrack fires when a dependency is collected; onTrigger fires when a dependency change schedules the recompute. Both receive a DebuggerEvent you can log or debugger-break on.

import { computed } from 'vue'

const total = computed(
  () => items.value.reduce((sum, i) => sum + i.price, 0),
  {
    onTrack(e) {
      // e.target, e.type, e.key -- which dependency was read
      console.log('tracked', e)
    },
    onTrigger(e) {
      // e.oldValue, e.newValue -- what changed
      debugger
    },
  },
)

These hooks are stripped from production builds. The advanced debugging APIs are listed under computed() and Reactivity Fundamentals.

See also