---
title: "Key rows by their ID · Agent Wranglers"
url: https://agentwranglers.se/playbook/app-ui/lists-and-tables/stable-row-keys
updated: 2026-10-20
licence: "All rights reserved"
---

# Key rows by their ID

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Lists and tables](https://agentwranglers.se/playbook/app-ui/lists-and-tables)
- Rule: Key every rendered list item by the record's stable ID, never by its position in the list.
- How: Frameworks reuse elements by key. With position as the key, deleting row 2 moves row 3's checkbox, focus and typed text onto the wrong record. Use the ID the server gave the record. For new records that have none yet, create a client ID once when the record is created.
- Check: A lint rule bans the index as a key, such as `react/no-array-index-key` from eslint-plugin-react. Where no such rule exists, a search for `:key="index"` or `key={i}` finds nothing. A browser test types into the row for one record, removes the row above it, and the typed text stays with its record.

Example:

```tsx
{orders.map(order => <OrderRow key={order.id} order={order} />)}
```

```vue
<OrderRow v-for="order in orders" :key="order.id" :order="order" />
```

```python
from playwright.sync_api import expect

def test_typed_text_stays_with_its_row(page):
    page.goto("https://staging.example.com/orders/A-1042/lines")
    rows = page.get_by_role("row")
    rows.nth(3).get_by_label("Note").fill("Gift wrap")
    rows.nth(2).get_by_role("button", name="Remove").click()
    expect(page.get_by_role("row", name="Blue mug")).to_contain_text("Gift wrap")
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers app interface playbook: https://agentwranglers.se/playbook/app-ui/lists-and-tables/stable-row-keys`.

- Previous: [Use grid keys only for editable grids](https://agentwranglers.se/playbook/app-ui/lists-and-tables/grid-keyboard)
- Next: [Say what select all selects](https://agentwranglers.se/playbook/app-ui/lists-and-tables/bulk-selection)
- [Lists and tables](https://agentwranglers.se/playbook/app-ui/lists-and-tables)
