DEV Community

Cover image for Vue Composition API: Computed and Ref Properties Explained
Lou Creemers
Lou Creemers

Posted on

Vue Composition API: Computed and Ref Properties Explained

Hey lovely readers,

If you are working with Vue 3 and TypeScript, you have probably seen ref and computed many times. They appear in tutorials, real projects, and even job interviews. When I first started using Vue 3, I kept wondering why everything had to be wrapped in ref, and what made computed different from a normal function.

I would consider myself more of a backend developer, and I spent a lot of time in 2025 trying to wrap my head around frontend development. So if you are using Vue 3 with TypeScript now, or planning to start learning Vue, this guide will help you clearly understand these two important concepts.

What is state?

Before we talk about ref and computed, we need to understand one important concept: state.

State is simply data that can change over time and affects what the user sees on the screen. If the data changes and the UI must update because of it, that data is state.

Examples of state in a frontend application include a counter value, a list of users fetched from an API, whether a modal is open or closed, the content of an input field, or the currently logged in user.

State from a backend perspective

If you come from a backend background, you are already familiar with state. You work with database records, request and response objects, session data, and cached values. That is all state too. The difference is where it lives.

In traditional backend driven applications, the flow usually looks like this:

  1. The client sends a request
  2. The server processes data
  3. The server returns HTML or JSON
  4. The page reloads with updated data

In this model, most of the state lives on the server.

In modern frontend frameworks like Vue, state often lives in the browser. There is no full page reload. Instead, a user clicks a button, data changes in memory, and the UI updates automatically. This automatic update is what we call reactivity.

Vue needs a way to know which data should trigger UI updates. That is exactly what ref and computed help with.

What are ref and computed in Vue 3?

In Vue 3, reactivity is managed with the Composition API. Two key features are ref and computed. You can think of ref as reactive state and computed as derived state.

ref as reactive state

You use ref to create reactive values such as numbers, strings, and booleans. This should be a value that you want to change when some action happens in the template.

Here is a simple example in Vue 3 with TypeScript:

import { ref } from 'vue'

const count = ref<number>(0)

function increment() {
  count.value++
}
Enter fullscreen mode Exit fullscreen mode

Here is what is happening. ref<number>(0) tells TypeScript that this ref contains a number. The actual value is accessed using .value. When count.value changes, Vue automatically updates the user interface.

In backend terms, you can think of ref as a variable stored in memory that Vue is actively watching. When the value changes, Vue reacts and updates the UI.

In the template, you do not need to use .value because Vue unwraps it for you:

<template>
  <p>{{ count }}</p>
  <button @click="increment">Increment</button>
</template>
Enter fullscreen mode Exit fullscreen mode

We use .value in the script because ref does not return the raw number. It returns a reactive wrapper object that allows Vue to track changes.

ref with objects

You can also use ref with objects, especially when working with TypeScript interfaces:

interface User {
  name: string
  age: number
}

const user = ref<User>({
  name: 'Lou',
  age: 27
})
Enter fullscreen mode Exit fullscreen mode

To update it:

user.value.age++
Enter fullscreen mode Exit fullscreen mode

If you mainly work with objects, you will also come across the reactive function, but the core idea stays the same. Vue tracks changes and updates the UI when the state changes.

Derived state and computed

If ref represents your source of truth, then computed represents values derived from that source.

Derived state is data that is calculated from other state, should always stay in sync, and should not be manually updated.

Here is an example:

import { ref, computed } from 'vue'

const price = ref<number>(10)
const quantity = ref<number>(2)

const total = computed<number>(() => {
  return price.value * quantity.value
})
Enter fullscreen mode Exit fullscreen mode

In this case, price and quantity are source state. total is derived state.

From a backend perspective, this is similar to a calculated column, a DTO field built from other fields, or a value computed from query results. You would not store total separately in a database if it can always be calculated from price * quantity, because that would create duplication and possible inconsistencies.

The same idea applies here. computed helps you avoid duplicated state and keeps your data consistent.

computed vs a function

You could write a normal function instead:

function fullName() {
  return `${firstName.value} ${lastName.value}`
}
Enter fullscreen mode Exit fullscreen mode

And use it in the template like this:

<p>{{ fullName() }}</p>
Enter fullscreen mode Exit fullscreen mode

So why use computed?

The main difference is caching and dependency tracking. A normal function runs every time the component re renders. It does not remember its previous result. Even if the underlying values have not changed, the function will still run again.

A computed property is cached. Vue tracks which reactive values it depends on and only recalculates when one of those values changes. If nothing changes, Vue reuses the previous result.

In small examples, the difference is not very noticeable. In larger components with complex logic or expensive calculations, computed makes your code more efficient and easier to reason about.

A simple rule that helps

When deciding between ref and computed, ask yourself one question:

Is this the source of truth, or is it derived from something else?

If it is the source of truth, use ref. If it is derived from other state, use computed.

This mental model is especially helpful if you come from a backend background where you already think in terms of data consistency and single sources of truth.

computed with getters and setters

Sometimes you want a computed property that can both read and update data. In that case, you can use a getter and a setter:

const fullName = computed<string>({
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  set(value: string) {
    const parts = value.split(' ')
    firstName.value = parts[0]
    lastName.value = parts[1]
  }
})
Enter fullscreen mode Exit fullscreen mode

Now you can assign a value directly:

fullName.value = 'Lou Creemers'
Enter fullscreen mode Exit fullscreen mode

This is useful in forms where one input field represents multiple pieces of state.

TypeScript tips

When using Vue 3 with TypeScript, it is good to think about types early. TypeScript often already recognizes the type automatically:

const isVisible = ref(true)
Enter fullscreen mode Exit fullscreen mode

But you can also define it explicitly:

const isVisible = ref<boolean>(true)
Enter fullscreen mode Exit fullscreen mode

For computed, the return type is usually recognized automatically. If needed, you can define it:

const total = computed<number>(() => {
  return price.value * quantity.value
})
Enter fullscreen mode Exit fullscreen mode

Being clear about types helps prevent subtle bugs, especially in larger projects or team environments.

Common mistakes

Here are some common mistakes that I've made dozens of times include:

  • Forgetting to use .value inside the script
  • Trying to change a computed property without defining a setter
  • using computed for something that should just be a normal constant.

If something is not updating in your template, the first thing to check is whether you are actually working with a reactive source.

That is a wrap

ref and computed are central parts of Vue 3’s reactivity model. Once you understand the difference between source state and derived state, your components become much easier to reason about. If you are building applications with Vue 3 and TypeScript, mastering these two concepts will give you a strong foundation for everything that follows.

If you have any questions or comments, feel free to reach out (louella.dev/socials) or leave a comment down below.

See ya!

Top comments (0)