remix-run/remix · warning

Duplicate keys detected in siblings: ${quotedKeys.join(', ')

Error message

Duplicate keys detected in siblings: ${quotedKeys.join(', ')}. Keys should be unique.

What it means

During reconciliation of keyed sibling children, the UI runtime detected the same key used more than once within one parent's children. Duplicate keys make reconciliation ambiguous and can cause children to be dropped, duplicated, or mis-patched.

Source

Thrown at packages/ui/src/runtime/reconcile.ts:1668

  for (let node of children) {
    if (node.key == null) continue

    if (!seenKeys) {
      seenKeys = new Set([node.key])
      continue
    }

    if (seenKeys.has(node.key)) {
      duplicateKeys ??= new Set()
      duplicateKeys.add(node.key)
    } else {
      seenKeys.add(node.key)
    }
  }

  if (duplicateKeys?.size) {
    let quotedKeys = Array.from(duplicateKeys, (key) => `"${String(key)}"`)
    console.warn(
      `Duplicate keys detected in siblings: ${quotedKeys.join(', ')}. Keys should be unique.`,
    )
  }
}

function patchKeyedChildren(
  curr: CommittedVNode[],
  next: VNodeInput[],
  domParent: ParentNode,
  vParent: VNodeParent,
  context: ReconcileContext,
  cursor?: HydrationCursor,
  anchor?: Node,
): CommittedVNode[] {
  let matches =
    matchKeyedChildrenInOrder(curr, next) ??
    matchKeyedChildrenAfterSingleRemoval(curr, next) ??
    matchKeyedChildrenAfterPairSwap(curr, next)

View on GitHub (pinned to 9696913134)

Solutions

  1. Ensure each sibling's key is unique within the parent, typically a stable record id
  2. Deduplicate source data before rendering
  3. If keys can collide legitimately, namespace them (e.g. `${type}-${id}`)

Example fix

// before
items.map((item) => <Row key={item.label} {...item} />)
// after
items.map((item) => <Row key={item.id} {...item} />)
Defensive patterns

Strategy: validation

Validate before calling

let keys = items.map((i) => i.id)
if (new Set(keys).size !== keys.length) throw new Error('duplicate keys in input data')

Type guard

const hasUniqueKeys = <T>(items: T[], key: (i: T) => string | number) =>
  new Set(items.map(key)).size === items.length

Prevention

When it happens

Trigger: Rendering an array of elements where two or more share the same key value, e.g. mapping data with non-unique ids, or mixing a hardcoded key like key="item" across dynamic children.

Common situations: Data with duplicate ids (e.g. two records sharing an id from a bad join or seed data), index-based keys with reordering plus a static key, or string/number key collisions ('1' vs 1 both stringified).

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/e46594f47d98259e. Report an issue: GitHub.