Why is this an issue?

Computed properties in Vue are meant to be pure derivations of reactive state: given the same inputs, they should always return the same value without altering anything else. When a computed property performs a side effect, such as mutating component data, pushing to an array, or triggering an asynchronous operation, the behavior becomes unpredictable.

Vue’s reactivity system re-evaluates computed properties whenever their dependencies change. Side effects inside a computed property therefore run at unexpected times, potentially producing stale or inconsistent application state that is difficult to reason about.

To avoid side effects in a computed property, either derive the value from a copy of the data without mutating the original, or move the side effect to a watch or watchEffect callback.

Code examples

Noncompliant code example

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

const items = ref([{ price: 3 }, { price: 1 }, { price: 2 }]);

const sortedItems = computed(() => {
  return items.value.sort((a, b) => a.price - b.price); // Noncompliant: mutating the reactive array in-place
});
</script>

Compliant solution

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

const items = ref([{ price: 3 }, { price: 1 }, { price: 2 }]);

const sortedItems = computed(() => {
  return items.value.toSorted((a, b) => a.price - b.price);
});
</script>

Noncompliant code example

<script>
export default {
  data() {
    return { items: [], discount: 0 };
  },
  computed: {
    totalPrice() {
      this.discount = this.items.length > 5 ? 10 : 0; // Noncompliant: mutating component data inside a computed property
      return this.items.reduce((sum, item) => sum + item.price, 0);
    }
  }
}
</script>

Compliant solution

<script>
export default {
  data() {
    return { items: [], discount: 0 };
  },
  computed: {
    totalPrice() {
      return this.items.reduce((sum, item) => sum + item.price, 0);
    }
  },
  watch: {
    items(newItems) {
      this.discount = newItems.length > 5 ? 10 : 0;
    }
  }
}
</script>

Resources

Documentation