Fixing “Each Child in a List Should Have a Unique key
Prop” in React 19
A forensic look at keys, reconciliation, and bullet‑proof patterns for dynamic routes & lists.
The Console Scream 😱
Warning: Each child in a list should have a unique "key" prop.
Check the render method of `AppRouter`. …
1 Why React Cares About Keys
Without key | With key |
---|---|
React reuses DOM nodes by index | React reuses nodes by identity |
Moving / filtering items can leak state (inputs swap, animations glitch) | Stable identity → predictable diff |
Keys act as the virtual DOM’s primary key.
2 Bug Anatomy
{routes.map(r => (
<Route path={r} element={<Page name={r} />} /> // 🔥 missing key
))}
Array item rendered without a key
prop triggers the warning.
3 Three Golden Rules
- Key must be stable across renders
- Key must be unique among siblings
- Never use array index unless list is truly static
4 Fixing Dynamic <Route>
Lists
{routes.map(r => (
<Route key={r} path={r} element={<Page name={r} />} />
))}
If objects contain IDs:
routeList.map(({ id, path, element }) => (
<Route key={id} path={path} element={element} />
));
5 Keys for Common UI Patterns
Pattern | Key suggestion |
---|---|
DB rows | row.id |
Tabs (i18n) | \${locale}-\${tabId} |
Drag‑and‑drop list | item.id |
Skeleton placeholders |
"skeleton-\${i}" (static order) |
File‑system routes | File path / slug |
6 Advanced Tips
Composite keys
key={`${userId}:${permission}`}
Memoised rows
const Row = memo(({ item }: { item: Item }) => …);
items.map(i => <Row key={i.id} item={i} />)
Keys with useDeferredValue
Keep keys on original data array, not the deferred copy.
7 Testing & Linting
- ESLint rule
react/jsx-key
(auto‑fix). - Jest snapshot diff catches DOM swaps.
- E2E tests assert order after drag actions.
8 Real‑World AppRouter
Fix
export const AppRouter = () => {
const routes = [
{ id: 'home', path: '/', el: <HomePage /> },
{ id: 'projects', path: '/projects', el: <ProjectPage /> },
{ id: 'settings', path: '/settings', el: <SettingsPage /> },
];
return (
<Routes>
{routes.map(({ id, path, el }) => (
<Route key={id} path={path} element={el} />
))}
</Routes>
);
};
✅ No warnings, ready for feature‑flag re‑ordering.
9 Cheat‑Sheet
Mistake | Fix |
---|---|
key={index} on dynamic list |
key={item.id} |
Missing key on mapped elements | Provide slug/ID |
Random Math.random() key |
Generates new key each render |
Duplicate keys | Use composite values |
Final Thoughts
Keys are tiny but crucial. For every .map()
ask:
- Unique?
- Stable?
✍️ Written by: Cristian Sifuentes – Full-stack dev crafting scalable apps with [NET - Azure], [Angular - React], Git, SQL & extensions. Clean code, dark themes, atomic commits
Follow those and your console will stay silent. Happy rendering! 🗝️🚀
Top comments (1)
This is very basic concept in React but one of the important ones.