Skip to content

Keys in Virtual DOM — Complete Guide

DodaTech Updated 2026-06-28 5 min read

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

  1. Using array index as key when the list order can change (causes unnecessary re-renders and state loss).
  2. Using Math.random() or Date.now() as key (forces full re-creation every render).
  3. Using the same key for multiple siblings (causes undefined behavior and bugs).
  4. Assuming keys are only needed for lists (they can be used on any element to control identity).
  5. Changing a component's key accidentally by using a volatile value from props.

Practice Questions

  1. What is a key in Virtual DOM? A unique identifier that helps the reconciler match elements across renders.
  2. What makes a good key? Stable, unique, predictable values like database IDs or UUIDs.
  3. What happens if you use array index as key? Items may be incorrectly matched when order changes, causing unnecessary DOM updates.
  4. 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

What is a key in Virtual DOM?

A key is a unique string identifier assigned to virtual nodes that helps the reconciler match them across renders.

Can I use array index as a key?

Only if the list is static and never reordered. For dynamic lists, index keys cause bugs.

Do keys affect performance?

Yes. Good keys enable O(1) lookups for item matching. Bad keys force full re-renders.

What happens if two siblings have the same key?

Behavior is undefined. The reconciler may incorrectly match or skip items.

Can keys be used on non-list elements?

Yes. Keys on any element control its identity across renders. Changing a key destroys and recreates the element.

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