Vue Composition API: Computed and Ref Properties Explained

Published: (February 19, 2026 at 03:44 PM EST)
6 min read
Source: Dev.to

Source: Dev.to

1️⃣ State – the foundation

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
  • the currently logged‑in user

If you come from a backend background, you are already familiar with state: database records, request/response objects, session data, cached values… The difference is where it lives.

Traditional backend‑driven flow

  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.

Modern frontend flow (Vue)

  • State lives in the browser.
  • No full page reload.
  • A user clicks a button → data changes in memory → UI updates automatically.

That automatic update is what we call reactivity. Vue needs a way to know which data should trigger UI updates – that’s exactly what ref and computed provide.

2️⃣ ref – reactive state

ref creates reactive values (numbers, strings, booleans, etc.) that Vue watches. When the value changes, Vue automatically updates the UI.

Basic example (Vue 3 + TypeScript)

import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}
  • ref(0) tells TypeScript that this ref contains a number.
  • The actual value is accessed via .value.
  • When count.value changes, Vue re‑renders any dependent UI.

Using it in a template

{{ count }}

<button @click="increment">Increment</button>

In the template you don’t need .value because Vue automatically unwraps the ref.

ref with objects

When dealing with objects (especially with TypeScript interfaces), you can still use ref.

interface User {
  name: string
  age: number
}

const user = ref({
  name: 'Lou',
  age: 27
})

// Updating a property
user.value.age++

If you mainly work with objects, you’ll also encounter the reactive function, but the core idea stays the same: Vue tracks changes and updates the UI when the state changes.

3️⃣ computed – derived (read‑only) state

If ref is your source of truth, computed represents values derived from that source. Derived state is calculated from other state, should always stay in sync, and should not be manually updated.

Simple derived state example

import { ref, computed } from 'vue'

const price = ref(10)
const quantity = ref(2)

const total = computed(() => {
  return price.value * quantity.value
})
  • price & quantity are source state (ref).
  • total is derived state (computed).

From a backend perspective, this is similar to a calculated column or a DTO field built from other fields. You wouldn’t store total separately in a database because it can always be derived from price * quantity; doing so would create duplication and possible inconsistencies. computed does the same thing on the client side.

computed vs. a plain function

function fullName() {
  return `${firstName.value} ${lastName.value}`
}
{{ fullName() }}

Why prefer computed?

Normal functioncomputed
Runs on each render✅ (always)❌ (only when deps change)
CachingNoYes (cached)
Dependency trackingManualAutomatic

A normal function runs every time the component re‑renders, even if the underlying values haven’t changed. A computed property is cached; Vue tracks which reactive values it depends on and only recomputes when one of those values changes. In larger components with expensive calculations, computed makes your code more efficient and easier to reason about.

Decision rule

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

  • Source of truth → use ref.
  • Derived → use computed.

This mental model aligns nicely with a backend mindset focused on data consistency and single sources of truth.

computed with getters and setters

Sometimes you need a computed property that can both read and write data. You can provide a getter and a setter:

import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  set(value: string) {
    const [first, ...rest] = value.split(' ')
    firstName.value = first
    lastName.value = rest.join(' ')
  }
})

Now fullName can be used like a regular reactive property:

First name: {{ firstName }}

Last name: {{ lastName }}

When the user edits the input, the setter updates firstName and lastName accordingly, and the UI stays in sync.

4️⃣ Recap

ConceptPurposeTypical use
refCreate reactive primitive or object stateCounters, toggles, API responses
computedDefine derived, cached state based on other refsCalculated totals, formatted strings, filtered lists
reactive (not covered in depth)Make deeply reactive objects (alternative to ref for objects)Complex nested state

Remember:

  • ref = source of truth (mutable).
  • computed = derived, read‑only (unless you add a setter).

Understanding these two building blocks will make you feel right at home with Vue 3’s reactivity system, no matter your backend background.

Happy coding! 🚀

Working with ref and computed in Vue 3 + TypeScript

watch(() => fullName.value, (value) => {
  const parts = value.split(' ')
  firstName.value = parts[0]
  lastName.value = parts[1]
})

Now you can assign a value directly:

fullName.value = 'Lou Creemers'

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

Types in Vue 3 + TypeScript

When using Vue 3 with TypeScript, it’s good to think about types early.
TypeScript can often infer the type automatically:

const isVisible = ref(true) // inferred as Ref<boolean>

You can also define it explicitly (the same syntax works):

const isVisible = ref(true)

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

const total = computed(() => {
  return price.value * quantity.value
})

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

Common Mistakes

  • 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 isn’t updating in your template, the first thing to check is whether you are actually working with a reactive source.

Why ref and computed Matter

ref and computed are central parts of Vue 3’s reactivity model.
Once you understand the difference between source state (ref) and derived state (computed), 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.

Got questions?

Feel free to reach out: louella.dev/socials or leave a comment down below.

See ya!

0 views
Back to Blog

Related posts

Read more »

Improving Accessibility - Tooltip

!Cover image for Improving Accessibility - Tooltiphttps://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev...