React Keys and Lists

Render dynamic collections with map and assign stable unique keys for predictable DOM reconciliation.

TL;DR

  1. Transform data arrays into component elements using standard map iterations.
  2. Assign stable unique identifiers to the top-level key property.
  3. Preserve component identity across DOM reorders by avoiding array index keys.

List Rendering Basics

    Array Map Iteration

    Map over items returning an element for each data record.

    export function TodoList({ todos }: TodoListProps) {
      return (
        <ListView>
          {todos.map((todo) => (
            <TodoRow key={todo.id} item={todo} />
          ))}
        </ListView>
      );
    }
    Separate Component Map

    Extract list item markup into a dedicated subcomponent.

    return (
      <Grid>
        {users.map((user) => (
          <UserCard key={user.id} user={user} />
        ))}
      </Grid>
    );
    Empty List Guard

    Provide informative placeholder when collection is empty.

    if (items.length === 0) {
      return <EmptyNotice text="No records found." />;
    }

Key Selection Rules

    Database ID Key

    Ideal stable unique identifier derived from backend data.

    items.map((item) => (
      <ItemRow key={item.uuid} item={item} />
    ))
    Keyed Fragment

    Assign key to React.Fragment when mapping multiple tags.

    import { Fragment } from 'react';
    items.map((item) => (
      <Fragment key={item.id}>
        <dt>{item.term}</dt>
        <dd>{item.def}</dd>
      </Fragment>
    ))
    Composite Key

    Combine stable unique fields when single IDs are absent.

    items.map((it) => (
      <Row key={`${it.category}-${it.code}`} />
    ))

List Transformation Patterns

    Filter and Map Pipeline

    Filter active items before mapping to JSX elements.

    const active = tasks
      .filter((t) => !t.completed)
      .map((t) => <Task key={t.id} task={t} />);
    Sorting Collections

    Clone array before sorting to avoid direct prop mutation.

    const sorted = [...items].sort((a, b) =>
      a.name.localeCompare(b.name)
    );
    Grouped Lists

    Map nested categories with independent keyed loops.

    categories.map((cat) => (
      <CategorySection key={cat.id}>
        <CategoryTitle title={cat.name} />
        {cat.items.map((it) => (
          <ItemRow key={it.id} title={it.title} />
        ))}
      </CategorySection>
    ))

Reconciliation Pitfalls

    Unstable Key Bug

    Random numbers force React to destroy and recreate DOM.

    // BAD: Destroys DOM and state
    items.map((item) => (
      <Row key={Math.random()}>{item.text}</Row>
    ))
    Index Key Danger

    Reordering causes inputs to retain wrong sibling state.

    // DANGEROUS: shifts state on delete
    items.map((item, idx) => (
      <InputRow key={idx} />
    ))
    Duplicate Key Error

    Duplicate keys trigger console warnings and rendering bugs.

    // Ensure IDs are distinct
    const unique = Array.from(new Set(ids));

Tips

  1. Always use natural database IDs or UUID strings as key values rather than relying on volatile loop indices.
  2. Attach the key property directly to the outermost element returned inside the map callback function.

Warnings

  1. Never use array indices as key props when collections can be sorted, filtered, or rearranged dynamically.
  2. Avoid generating random keys like Math.random() on each render because this completely destroys internal DOM state.

In Practice

FAQ