← Debugging Guides

阅读中文版本

Vue Array Mutation Not Reactive — UI Does Not Update

Fix non-reactive array updates in Vue by replacing the array or using reactive-aware methods, and avoid mutating index positions directly.

intermediatedebuggingvuereactivityarrays

Symptoms

  • Pushing to an array does not update the UI
  • Assigning by index (arr[0] = x) does not re-render
  • Changing array length does not trigger an update

Possible causes

  • A plain (non-reactive) array is mutated and Vue is unaware
  • Direct index assignment bypasses reactive tracking in some setups
  • The array is destructured into a plain variable, losing reactivity

Diagnosis steps

Confirm the array is reactive

import { isReactive } from 'vue'; console.log(isReactive(arr));

Interpretation

If false, the array was copied or created outside a reactive context.

Use a reactive-aware method

arr.value = [...arr.value, newItem]

Interpretation

Replacing the array reference reliably triggers reactivity in all setups.

Root cause

Vue 3 tracks reactive arrays through Proxy, and its instrumented array methods (push, splice, etc.) notify dependents. Mutations that bypass these mechanisms—such as assigning to an index of a plain array or copying the array into a non-reactive binding—do not notify the renderer.

Fix

Keep arrays inside ref/reactive, use Vue-aware methods, replace the whole array when in doubt (arr.value = [...arr.value, x]), and use toRefs or computed for derived views.

Risk note

Deep-watching large arrays is expensive; prefer computed or keyed updates over watch with deep:true.

Verify the fix

Mutate the array through a reactive-aware path and confirm the UI updates; assert the rendered list length matches the array length.

Symptoms

When you hit “Vue Array Mutation Not Reactive — UI Does Not Update”, guessing wastes time. The guide below follows evidence: reproduce the symptom, run targeted diagnostics, identify the root cause, apply the fix, and verify it with concrete checks.

Observed symptoms include:

  • Pushing to an array does not update the UI
  • Assigning by index (arr[0] = x) does not re-render
  • Changing array length does not trigger an update

Reproduce and Diagnose

Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.

Step 1: Confirm the array is reactive

import { isReactive } from 'vue'; console.log(isReactive(arr));

If false, the array was copied or created outside a reactive context.

Step 2: Use a reactive-aware method

arr.value = [...arr.value, newItem]

Replacing the array reference reliably triggers reactivity in all setups.

Likely contributors, in order of frequency:

  • A plain (non-reactive) array is mutated and Vue is unaware
  • Direct index assignment bypasses reactive tracking in some setups
  • The array is destructured into a plain variable, losing reactivity

Root Cause

Vue 3 tracks reactive arrays through Proxy, and its instrumented array methods (push, splice, etc.) notify dependents. Mutations that bypass these mechanisms—such as assigning to an index of a plain array or copying the array into a non-reactive binding—do not notify the renderer.

The Fix

Keep arrays inside ref/reactive, use Vue-aware methods, replace the whole array when in doubt (arr.value = […arr.value, x]), and use toRefs or computed for derived views.

Watch out: Deep-watching large arrays is expensive; prefer computed or keyed updates over watch with deep:true.

<script setup>
import { ref } from 'vue';
const items = ref([]);

function add(item) {
  items.value.push(item);          // instrumented, reactive
}
function updateFirst(value) {
  items.value = [value, ...items.value.slice(1)]; // replace to be safe
}
</script>

<template>
  <ul><li v-for="i in items" :key="i.id">{{ i.name }}</li></ul>
</template>

Verify and Prevent

Mutate the array through a reactive-aware path and confirm the UI updates; assert the rendered list length matches the array length.

Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.

This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.