Files
agent-skills/skills/vue-best-practices/reference/computed-return-value-readonly.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

4.0 KiB

title, impact, impactDescription, type, tags
title impact impactDescription type tags
Never Mutate Computed Property Return Values HIGH Mutating computed values causes silent failures and lost changes capability
vue3
computed
reactivity
immutability
common-mistake

Never Mutate Computed Property Return Values

Impact: HIGH - The returned value from a computed property is derived state - a temporary snapshot. Mutating this value leads to bugs that are difficult to debug.

Important: Mutations DO persist while the computed cache remains valid, but are lost when recomputation occurs. The danger lies in unpredictable cache invalidation timing - any change to the computed's dependencies triggers recomputation, silently discarding your mutations. This makes bugs intermittent and hard to reproduce.

Every time the source state changes, a new snapshot is created. Mutating a snapshot is meaningless because it will be discarded on the next recalculation.

Task Checklist

  • Treat computed return values as read-only
  • Update the source state instead of the computed value
  • Use writable computed properties if bidirectional binding is needed
  • Avoid array mutating methods (push, pop, splice, reverse, sort) on computed arrays

Incorrect:

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

const books = ref(['Vue Guide', 'React Handbook'])

const publishedBooks = computed(() => {
  return books.value.filter(book => book.includes('Guide'))
})

function addBook() {
  // BAD: Mutating computed value - change will be lost!
  publishedBooks.value.push('New Book')
}

// BAD: Mutating computed array
const sortedBooks = computed(() => books.value.filter(b => b))

function reverseBooks() {
  // BAD: This mutates the computed snapshot
  sortedBooks.value.reverse()
}
</script>
<script>
export default {
  data() {
    return {
      author: {
        name: 'John',
        books: ['Book A', 'Book B']
      }
    }
  },
  computed: {
    authorBooks() {
      return this.author.books
    }
  },
  methods: {
    addBook() {
      // BAD: Mutating computed value
      this.authorBooks.push('New Book')
    }
  }
}
</script>

Correct:

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

const books = ref(['Vue Guide', 'React Handbook'])

const publishedBooks = computed(() => {
  return books.value.filter(book => book.includes('Guide'))
})

function addBook(bookName) {
  // GOOD: Update the source state
  books.value.push(bookName)
}

// GOOD: Create a copy before mutating for display
const sortedBooks = computed(() => {
  return [...books.value].sort()  // Spread to create copy before sort
})

const reversedBooks = computed(() => {
  return [...books.value].reverse()  // Spread to create copy before reverse
})
</script>
<script>
export default {
  data() {
    return {
      author: {
        name: 'John',
        books: ['Book A', 'Book B']
      }
    }
  },
  computed: {
    authorBooks() {
      return this.author.books
    }
  },
  methods: {
    addBook(bookName) {
      // GOOD: Update source state
      this.author.books.push(bookName)
    }
  }
}
</script>

Writable Computed for Bidirectional Binding

If you genuinely need to "set" a computed value, use a writable computed property:

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

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

// Writable computed with getter and setter
const fullName = computed({
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  set(newValue) {
    // Update source state based on the new value
    const parts = newValue.split(' ')
    firstName.value = parts[0] || ''
    lastName.value = parts[1] || ''
  }
})

// Now this is valid:
fullName.value = 'Jane Smith'  // Updates firstName and lastName
</script>

Reference