After deleting a row in a Vue list, why do input values shift to another row—is the v-for key wrong?
Conclusion first: it is not the input that shifts, it is a v-for key identity mismatch
In Vue projects in 2026, when input values in the rows below shift after a row is deleted from a list, the usual cause is not that the input component is broken. It is that the v-for key uses the array index. Vue determines node identity by key. The index changes with deletion, sorting, and filtering, so the DOM and component instance that originally belonged to row A get reused for row B, and uncontrolled input state moves with them. Switching the key to a stable business id resolves most cases. If the data itself has no stable id, add one at the data layer first.
Why deleting a row causes shifting: what the virtual DOM reuses
Vue list updates do not destroy the whole segment and rebuild it. Instead, Vue matches old and new nodes by key. If the key is the same, the original DOM element and component instance are reused, and only changed parts are updated, which improves performance. The problem is identity: when the key is the index, after deleting the first item, the node at original index 0 still exists. Vue believes it is still the original item, so it renders new data B into A's input, while the value the user already typed, the cursor position, and the third-party component's internal cache are not reset. Visually, the rows below appear to have shifted content.
Here you need to distinguish two kinds of state. If the input value is bound to the list data itself via v-model, the data moves with deletion, and shifting generally does not happen. What is more likely to shift is uncontrolled DOM values, component-private state, and expanded or selected caches. With the same v-for, some projects look fine, while others break as soon as a row is deleted. The difference is not the framework version but which layer holds the state.
- Will shift: the input stores its own value internally, the component maintains edit state internally, or a third-party component caches the in-row index.
- Less likely to shift: the value is fully driven by parent data, and the list item is a pure presentational component.
- More likely to shift: after deleting a middle item, sorting or filtering is also applied, making the index identity mismatch more obvious.
Four-point checklist for list rendering
Rather than changing the key by feel, check in a fixed order. The four-point checklist for list rendering below follows 2026 project delivery practice: the first two points establish identity, and the last two establish state ownership and fallback measures. Skipping any point tends to cause repeated rework.
- Does the data have a stable unique id? First check whether the API response includes a business primary key. If not, ask the backend to add one, or generate a stable identifier at the data entry point based on business fields. Do not use a random value that changes on every request as the id.
- Does the v-for key use this id? Check whether it is written as :key bound to index or whether the key is omitted. Four types of operations—middle insertion, deletion, sorting, and filtering—all change index identity.
- Where do input state and expanded state live? Is the value bound to the list data, or is it kept inside the component or on the DOM? If it stays inside, assess whether it needs to be destroyed with the row after deletion.
- Is forced re-creation needed? Only a few scenarios, such as switching data sources and needing to clear internal caches, should use a key change to force component re-creation. It is a fallback, not a regular pattern, and rebuilding the whole item has a performance cost.
The first step is often skipped. From experience, about 60% to 70% of list shifting problems can be solved by adding a stable id and changing the key. Only the rest require adjusting state ownership or the component re-creation strategy. What counts as acceptable: after deleting any middle item, the remaining rows' input values, expanded state, and selected state do not shift position.
What is the difference between index as key and business id as key?
Both approaches run, but they differ in whether identity is stable and in update cost. The comparison below uses three dimensions: applicable cases, common risks, and experience-based cost. Numbers are experience ranges; actual results depend on project complexity.
- Index as key: suitable for short lists that are purely static, appended only at the end, and never re-sorted. The risk is state shifting after insertion, deletion, or sorting. Code cost is low, but typical rework cost is about 0.5 to 2 working days.
- Business id as key: suitable for lists that can be added to, deleted from, edited, sorted, and filtered. The risk is that the id must be stable; otherwise components will be re-created frequently. It takes a little more time upfront to organize the data, but later changes and integration testing are easier.
- Random number as key: it looks unique, but it changes on every render, which effectively makes Vue rebuild the whole list. With heavier list items, this can cause input blur and animation jumps, so it is not recommended as a regular practice.
- Composite key: when there truly is no single primary key in the business domain, you can combine business fields into a stable value. The combined fields must be genuinely unique; otherwise it is equivalent to not changing the key.
Do not judge by lines of code. Judge by four operations: deleting from the middle, inserting at the head, reversing the order, and filtering by condition. If state does not shift after all four, the key is correct.
Delivery scenario: validation shifts after deleting a row in an admin form list
In an admin form project, the constraints were common: a two-week schedule, list fields concatenated and returned by the backend, the frontend initially rendering by array index, and inputs using a third-party component's internal values. During development, only the first screen and appending at the end were tested, and everything looked fine. During integration testing, deleting the second row caused the third row's validation message and input value to move up, and submitted data was misaligned. Typical troubleshooting plus rework took about 1 to 2 working days. The fix was to have the backend add a business primary key, switch the frontend key to that primary key, and move edit state back into the list data. After that, deletion, sorting, and filtering no longer caused shifting. This cost shows that key is not a styling detail but the list identity contract.
Common pitfalls and acceptance criteria
The pitfalls below still appear in 2026 projects, ordered by frequency and impact. For acceptance, you do not need to read all the code; just walk through the operation path.
- Using index with transition animations: after deletion, the animation attaches to the wrong node, causing flicker or misalignment. Fix the key first, then adjust the animation.
- List item component watching index internally: when an in-row component uses the index to determine whether it is the first item, the judgment becomes wrong after deletion. Pass a business id or boolean flag instead.
- Not resetting input state after filtering: if a filtered-out row is reused, the input may retain the previous value. Filtering is an identity-change scenario, so rebuild according to the new list or match by id.
- Testing only the first screen and appending: this is a common reason shifting problems reach production. Test cases should cover four operations: middle deletion, head insertion, reverse order, and filtering.
Acceptance criteria can be written as three points: after deleting a middle item, the remaining rows' state does not move; after sorting and filtering, previous input values do not appear; submitted data corresponds one-to-one with the currently displayed rows. Only when all three pass is list rendering acceptable.
Applicable and non-applicable boundaries
The key strategy for list rendering is not about being as complex as possible; it should match the list lifecycle. Whenever a list can have middle insertions or deletions, sorting, or filtering, use a stable business id as the key. If the list renders once and does not change afterward, the negative impact of index is small, but using id consistently is still recommended to avoid pitfalls when requirements change later.
- Suitable for business id first: editable tables, approval workflow lists, shopping carts, chat histories, and tree lists with expandable rows.
- Narrow cases where index can be used: data comes from static configuration, renders only once, and contains no user input or internal state.
- Cases that do not need extra solutions: when the list is very small and only displays data, there is no need to introduce virtual scrolling or complex key-generation logic.
- Not applicable: forcing the whole list to re-create just to hide a key problem. With heavier list items, this causes input blur and noticeable lag.
FAQ
What happens if v-for does not have a key in Vue?
Without a key, Vue reuses in place by default, and state can still shift when insertion, deletion, or order changes occur. The purpose of key is to give nodes stable identity; add it whenever possible.
If index is used as the key and the list only appends without deleting, will there be problems?
When appending only at the end and not sorting or filtering, index matches the data order, so problems are unlikely. Once there is middle insertion or deletion, identity becomes mismatched.
The input is already bound to data with v-model, so why does it still shift?
Values bound to data do not shift. What usually shifts is component-private state, uncontrolled DOM values, or third-party component caches. Move that state up to the data layer.
After switching to a unique id, list updates become slower—is it a key problem?
Usually it is not the key itself, but an unstable id causing frequent component re-creation, or overly heavy list items. First check whether the id changes on every request, then consider virtual scrolling.
The list data has no unique id—what is the common practice in 2026?
Ask the backend to add a primary key, or generate a stable composite value at the data entry point based on business fields. Random numbers are not recommended because they make Vue rebuild the whole list on every render.
In practice, first find a stable primary key in the list data, switch the v-for key to it, and then run acceptance using four operations: middle deletion, head insertion, reverse order, and filtering. If the data has no primary key yet, have the API add one; do not rely on the index on the frontend. The applicable boundary is: mutable lists should use a business id, static lists that render only once can be more relaxed, but do not use random numbers as keys.
-
Front-End Interactive Development (Website/H5/APP/Mini-Program): A 2026 Implementation Guide
Date: Aug 3, 2026 Read: 76
-
A Guide to State Management Solutions in Frontend Interaction
Date: Jul 21, 2026 Read: 89
-
An H5 campaign page embedded in Flutter scrolls rough on Android—which layer should you touch first?
Date: Oct 14, 2026 Read: 5
-
It's 2026 and the page title isn't in the source code — where does Baidu get the indexed title?
Date: Oct 13, 2026 Read: 9
-
OnPush child component not refreshing with polling data in Angular: replace the array reference first, or add markForCheck first?
Date: Oct 12, 2026 Read: 14




