remix-run/remix · warning
Duplicate keys detected in siblings
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.
Solutions
- Ensure each sibling's key is unique within the parent, typically a stable record id
- Deduplicate source data before rendering
- 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
- Always key by stable unique record ids
- Deduplicate fetched data before rendering
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
- cascading component updates detected in one event loop…
- popover.surface() requires a registered anchor before…
- render called after component was removed, potential…
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/e46594f47d98259e.
Report an issue: GitHub.
Appendix: 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)