Keys in Virtual DOM — Complete Guide
In this tutorial, you will learn about Keys in Virtual Dom. We cover key concepts, practical examples, and best practices to help you master this topic.
Keys in Virtual DOM help the reconciler identify list items across renders, enabling efficient reordering, insertion, and removal without unnecessary DOM operations.
What You'll Learn
- What keys are and why the reconciler needs them
- How to choose good key values
- What happens when keys are missing or incorrect
- How keys affect component state preservation
Why It Matters
Wrong or missing keys cause full list re-renders, lost component state, and incorrect DOM behavior. Correct keys make list rendering efficient and reliable.
flowchart LR
A[List Rendering] --> B{Keys provided?}
B -->|Yes| C[Keyed reconciliation]
B -->|No| D[Positional matching]
C --> E[Items matched by identity]
D --> F[Items matched by index]
E --> G[Minimal DOM moves]
F --> H[Possible full re-render]
G --> I[State preserved correctly]
H --> J[State may be lost]
Why Keys Matter
Without keys, the reconciler matches children by position. Adding an item at the beginning shifts all subsequent items, causing unnecessary DOM updates.
// Without keys: positional matching causes issues
function ListWithoutKeys(items) {
return {
type: 'ul',
props: {},
children: items.map(item => ({
type: 'li',
props: {},
children: [item.text]
}))
};
}
// Initial render: [A, B, C, D]
// After inserting 'X' at index 0: [X, A, B, C, D]
// Without keys, the reconciler sees:
// li[0]: A -> X (text update)
// li[1]: B -> A (text update)
// li[2]: C -> B (text update)
// li[3]: D -> C (text update)
// li[4]: (new) -> D (add)
// Result: 4 text updates + 1 add (5 operations)
// Ideal: 1 add + 0 updates (1 operation)
With Keys: Efficient Reconciliation
Keys tell the reconciler which items correspond across renders.
function ListWithKeys(items) {
return {
type: 'ul',
props: {},
children: items.map(item => ({
type: 'li',
key: item.id, // Stable, unique identifier
props: {},
children: [item.text]
}))
};
}
// Initial render: [{id:1,text:'A'}, {id:2,text:'B'}, {id:3,text:'C'}, {id:4,text:'D'}]
// After inserting at index 0: [{id:5,text:'X'}, {id:1,text:'A'}, {id:2,text:'B'}, {id:3,text:'C'}, {id:4,text:'D'}]
// With keys, the reconciler matches by id:
// id:5 is new -> add li at index 0
// id:1,2,3,4 exist -> no update needed
// Result: 1 add, 0 updates (ideal)
Choosing Good Keys
Good keys are stable, unique, and predictable. Bad keys cause full re-renders or bugs.
// GOOD: Stable unique identifier from data
items.map(item => <li key={item.id}>{item.name}</li>);
// GOOD: Stable string from data
items.map(item => <li key={item.slug}>{item.title}</li>);
// BAD: Array index (breaks when order changes)
items.map((item, index) => <li key={index}>{item.name}</li>);
// BAD: Random value (creates new identity every render)
items.map(item => <li key={Math.random()}>{item.name}</li>);
// BAD: Timestamp (different every render)
items.map(item => <li key={Date.now()}>{item.name}</li>);
// BAD: Shallow object (string representation changes)
items.map(item => <li key={JSON.stringify(item)}>{item.name}</li>);
Keys and Component State
Keys determine whether a component instance is preserved or destroyed. Changing a key destroys the old instance and creates a new one.
// Component state is tied to the key
class Counter extends Component {
constructor() {
super();
this.state = { count: 0 };
}
render() {
return {
type: 'div',
props: {},
children: [
{ type: 'span', props: {}, children: ['Count: ' + this.state.count] },
{ type: 'button', props: { onClick: () => this.setState({ count: this.state.count + 1 }) }, children: ['+'] }
]
};
}
}
// If key changes, Counter is destroyed and recreated:
// <Counter key={user.id} /> // user.id changes -> new Counter instance
// The old Counter's count state is lost.
// The new Counter starts with count: 0.
// If key stays the same, Counter instance is preserved:
// <Counter key="unique-counter" /> // Stable key
// Counter retains its count state across re-renders.
Keyed Children in Practice
Frameworks use keys differently. React requires explicit keys on elements. Vue automatically assigns keys in v-for.
// React: explicit key on each mapped element
function ItemList({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
// Vue: key in v-for
// <li v-for="item in items" :key="item.id">{{ item.name }}</li>
// In a generic virtual DOM:
function renderList(items, renderItem, getKey) {
return {
type: 'div',
props: {},
children: items.map((item, index) => {
const child = renderItem(item, index);
child.key = getKey(item, index);
return child;
})
};
}
Common Mistakes
- Using array index as key when the list order can change (causes unnecessary re-renders and state loss).
- Using Math.random() or Date.now() as key (forces full re-creation every render).
- Using the same key for multiple siblings (causes undefined behavior and bugs).
- Assuming keys are only needed for lists (they can be used on any element to control identity).
- Changing a component's key accidentally by using a volatile value from props.
Practice Questions
- What is a key in Virtual DOM? A unique identifier that helps the reconciler match elements across renders.
- What makes a good key? Stable, unique, predictable values like database IDs or UUIDs.
- What happens if you use array index as key? Items may be incorrectly matched when order changes, causing unnecessary DOM updates.
- How do keys affect component state? Changing a key destroys the old component instance and creates a new one, losing state.
Challenge
Build a todo list with a bug: use array index as key. Add features to insert items at the beginning and reorder items. Observe the incorrect behavior (wrong items being deleted, input state lost). Then fix it by using stable IDs as keys. Show both implementations side by side.
FAQ
Mini Project
Build a sortable list component that demonstrates the key problem. The list should allow adding items, removing items, and sorting. Use index keys first to show the bugs (wrong items deleted, animations broken). Then switch to stable ID keys to show correct behavior.
What's Next
Lesson 5: Virtual DOM vs Real DOM
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro