Chapter 7 of 11

Vue 3 tutorial: computed properties and how caching works

verified on 2 September 2026 · 4 min

Quick answer

computed() declares a value derived from other values. Vue caches the result and only recalculates it when a dependency changes, unlike a function called from the template, which runs on every render. A computed property has to be pure: it reads and returns, with no side effect.

By the end of this chapter, you will know how to derive a value from the state instead of keeping it up to date by hand.

A computed property is a value derived from other values. Vue recalculates it when one of its dependencies changes, and only then. It is the tool that keeps logic out of the template.

A first computation

The product page has to show the brand and the product. You could do it in the template:

html
<h1>{{ brand + ' ' + product }}</h1>

It works, but the logic scatters through the HTML. Declare a computed property instead:

src/App.vue
<script setup>
import { computed, ref } from 'vue'

const product = ref('T-Shirt')
const brand = ref('Gekkode')

const title = computed(() => `${brand.value} ${product.value}`)
</script>

<template>
  <h1>{{ title }}</h1>
</template>

computed takes a function and returns a read-only ref. In the script you read title.value, in the template, {{ title }}, just like a ref.

Caching

This is the real difference with an ordinary function. A computed property holds on to its result and only recalculates it when one of its dependencies has changed.

html
<!-- appelée à chaque rendu, même si rien n’a changé -->
<p>{{ calculerTitre() }}</p>

<!-- calculée une fois, puis relue depuis le cache -->
<p>{{ title }}</p>

On a concatenation, the difference is nil. On a sort or a filter applied to several hundred entries, and shown in three places on the page, it becomes obvious.

Driving the selected variant

On to a useful case. Each variant now has its own stock quantity:

src/App.vue
<script setup>
const variants = ref([
  { id: 2234, color: '#2563eb', image: '/images/t-shirt-bleu.svg', quantity: 20 },
  { id: 2235, color: '#dc2626', image: '/images/t-shirt-rouge.svg', quantity: 0 }
])
</script>

The blue one is available, the red one is not. The image shown and the availability now both depend on the variant being hovered. Rather than keeping two variables up to date by hand, let us hold on to a single piece of information: which one is selected.

src/App.vue
<script setup>
const selectedVariant = ref(0)

function updateVariant(index) {
  selectedVariant.value = index
}
</script>

The loop passes on the index, picked up as in chapter 4:

src/App.vue
<div
  v-for="(variant, index) in variants"
  :key="variant.id"
  class="color-circle"
  :style="{ backgroundColor: variant.color }"
  @mouseover="updateVariant(index)"
></div>

The rest follows from it. image and inStock are no longer data but computations:

src/App.vue
<script setup>
const image = computed(() => variants.value[selectedVariant.value].image)
const inStock = computed(() => variants.value[selectedVariant.value].quantity > 0)
</script>

Hover over the red swatch: the image changes, the message switches to “En rupture”, and the button greys out thanks to the class binding. One variable moved, the three displays followed.

Return a boolean, not a number

Writing computed(() => variants.value[selectedVariant.value].quantity) looks like it works: a quantity of 0 is falsy, a quantity of 20 is truthy. But inStock then holds 20, and the day you display it with {{ inStock }}, the page will show “20”. Compare explicitly with > 0.

When to use a computed property

  • For a value derived from state: a basket total, a filtered list, an assembled string. That is the normal case.
  • Not for a side effect. A computed property has to be a pure function: it reads, it returns. It changes nothing, calls no API, fires no request. That is what watch and watchEffect are for.
  • Not for a value that changes on its own, such as the current time: nothing would tell Vue to recalculate.

A writable computed property

By default a computed property is read-only. Vue warns in the console if you try to assign to it. For the cases where writing makes sense, provide a get / set pair:

javascript
const nomComplet = computed({
  get: () => `${prenom.value} ${nom.value}`,
  set: (valeur) => {
    [prenom.value, nom.value] = valeur.split(' ')
  }
})
In the Options API

Computed properties live in the computed option and read state through this: computed: { title() { return this.brand + ' ' + this.product } }. The caching principle is the same.

ExerciseAdd a computed property called resume that returns “Gekkode T-Shirt, blue, in stock” from the selected variant. Then check with a console.log inside the function that it is not recalculated on every render.

Common errors

Returning a number instead of a boolean A quantity of 0 is falsy and a quantity of 20 is truthy, so the code looks like it works. But displaying the value would show “20”. Compare explicitly: quantity > 0.
Putting a side effect in a computed property A network call or a state change would fire there unpredictably. Use watch or watchEffect.
Assigning a value to a computed property It is read-only by default, Vue warns in the console. Provide a get/set pair if writing makes sense.
Newsletter

New tests, tutorials and projects, by e-mail.

Reproducible tests, versioned code, dated results. Never any spam.