Fix Warning: Each child in a list should have a unique "key" prop in React
This warning fires when you render a list of elements without assigning a unique key prop to each item. React uses keys to track which items changed, were added, or removed. Fix it by adding a stable, unique key such as a database ID to each list element instead of using array indices.
Reading the Stack Trace
Here's what each line means:
- at ProductCard (webpack-internal:///./src/components/ProductCard.tsx:6:3): ProductCard is the child element being rendered without a key prop inside the parent list.
- at ProductList (webpack-internal:///./src/components/ProductList.tsx:11:5): ProductList is where .map() is called to render multiple ProductCard elements without unique keys.
- at beginWork (node_modules/react-dom/cjs/react-dom.development.js:21587:16): React's reconciler is processing the component tree and detected missing key props during diffing.
Common Causes
1. No key prop on mapped elements
The .map() call renders components without passing a key prop, so React cannot efficiently reconcile list changes.
export default function ProductList({ products }) {
return (
<ul>
{products.map(product => (
<ProductCard name={product.name} price={product.price} />
))}
</ul>
);
}
2. Using array index as key
Using the array index as key causes bugs when items are reordered, inserted, or deleted because indices shift and React reuses the wrong DOM nodes.
export default function ProductList({ products }) {
return (
<ul>
{products.map((product, index) => (
<ProductCard key={index} name={product.name} price={product.price} />
))}
</ul>
);
}
3. Duplicate keys in the list
Two or more items share the same key value, causing React to misidentify elements and produce rendering glitches.
export default function ProductList({ products }) {
return (
<ul>
{products.map(product => (
<ProductCard key={product.category} name={product.name} />
))}
</ul>
);
}
The Fix
Add a unique and stable key prop to each ProductCard element using the product's database ID. This allows React to correctly identify which items changed and avoid unnecessary re-renders or DOM corruption.
export default function ProductList({ products }) {
return (
<ul>
{products.map(product => (
<ProductCard name={product.name} price={product.price} />
))}
</ul>
);
}
export default function ProductList({ products }) {
return (
<ul>
{products.map(product => (
<ProductCard key={product.id} name={product.name} price={product.price} />
))}
</ul>
);
}
Testing the Fix
import { render, screen } from '@testing-library/react';
import ProductList from './ProductList';
describe('ProductList', () => {
const products = [
{ id: '1', name: 'Widget', price: 9.99 },
{ id: '2', name: 'Gadget', price: 19.99 },
];
it('renders all products without key warnings', () => {
const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<ProductList products={products} />);
expect(spy).not.toHaveBeenCalledWith(
expect.stringContaining('unique "key" prop')
);
spy.mockRestore();
});
it('renders each product name', () => {
render(<ProductList products={products} />);
expect(screen.getByText('Widget')).toBeInTheDocument();
expect(screen.getByText('Gadget')).toBeInTheDocument();
});
it('handles an empty product list', () => {
render(<ProductList products={[]} />);
expect(screen.queryByRole('listitem')).not.toBeInTheDocument();
});
});
Run your tests:
npm test
Pushing Through CI/CD
git checkout -b fix/react-key-prop-error,git add src/components/ProductList.tsx src/components/__tests__/ProductList.test.tsx,git commit -m "fix: add unique key prop to mapped ProductCard elements",git push origin fix/react-key-prop-error
Your CI config should look something like this:
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
- run: npm run build
The Full Manual Process: 18 Steps
Here's every step you just went through to fix this one bug:
- Notice the error alert or see it in your monitoring tool
- Open the error dashboard and read the stack trace
- Identify the file and line number from the stack trace
- Open your IDE and navigate to the file
- Read the surrounding code to understand context
- Reproduce the error locally
- Identify the root cause
- Write the fix
- Run the test suite locally
- Fix any failing tests
- Write new tests covering the edge case
- Run the full test suite again
- Create a new git branch
- Commit and push your changes
- Open a pull request
- Wait for code review
- Merge and deploy to production
- Monitor production to confirm the error is resolved
Total time: 30-60 minutes. For one bug.
Or Let bugstack Fix It in Under 2 minutes
Every step above? bugstack does it automatically.
Step 1: Install the SDK
npm install bugstack-sdk
Step 2: Initialize
import { initBugStack } from 'bugstack-sdk'
initBugStack({ apiKey: process.env.BUGSTACK_API_KEY })
Step 3: There is no step 3.
bugstack handles everything from here:
- Captures the stack trace and request context
- Pulls the relevant source files from your GitHub repo
- Analyzes the error and understands the code context
- Generates a minimal, verified fix
- Runs your existing test suite
- Pushes through your CI/CD pipeline
- Deploys to production (or opens a PR for review)
Time from error to fix deployed: Under 2 minutes.
Human involvement: zero.
Try bugstack Free →No credit card. 5-minute setup. Cancel anytime.
Deploying the Fix (Manual Path)
- Run the test suite locally to confirm no key prop warnings appear.
- Open a pull request with the key prop additions.
- Wait for CI checks to pass on the PR.
- Have a teammate review and approve the PR.
- Merge to main and verify the fix in staging before promoting to production.
Frequently Asked Questions
BugStack runs the fix through your existing test suite, generates additional edge-case tests for list rendering, and validates that no other components are affected before marking it safe to deploy.
BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review it before merging.
Index keys cause subtle bugs when items are reordered, inserted, or deleted. React will reuse DOM nodes for the wrong data, leading to stale input values and broken animations.
You can generate stable keys using a combination of unique fields, such as name plus timestamp. As a last resort, use a library like uuid to assign IDs when the data is first created.