---
title: Use data-testid for Dynamic Elements
impact: CRITICAL
impactDescription: stable selectors for dynamic content
tags: loc, data-testid, testing, selectors, attributes
---

## Use data-testid for Dynamic Elements

When elements lack accessible names or roles, use `data-testid` attributes. These are explicitly for testing and won't change during normal development.

**Incorrect (fragile selectors for dynamic content):**

```typescript
// tests/dashboard.spec.ts
test('shows user stats', async ({ page }) => {
  await page.goto('/dashboard');

  // Breaks when text formatting changes
  await expect(page.locator('text=Active Users: 42')).toBeVisible();

  // Breaks when CSS class changes
  await expect(page.locator('.stat-card:first-child .value')).toHaveText('42');

  // Breaks when order changes
  await expect(page.locator('div:nth-child(3)')).toContainText('Revenue');
});
```

**Correct (data-testid for dynamic elements):**

```typescript
// components/StatsCard.tsx
export function StatsCard({ label, value }: Props) {
  return (
    <div className="stat-card" data-testid={`stat-${label.toLowerCase()}`}>
      <span className="label">{label}</span>
      <span className="value" data-testid={`stat-${label.toLowerCase()}-value`}>
        {value}
      </span>
    </div>
  );
}

// tests/dashboard.spec.ts
test('shows user stats', async ({ page }) => {
  await page.goto('/dashboard');

  // Stable selectors using data-testid
  await expect(page.getByTestId('stat-active-users')).toBeVisible();
  await expect(page.getByTestId('stat-active-users-value')).toHaveText('42');
  await expect(page.getByTestId('stat-revenue')).toBeVisible();
});
```

**Custom test ID attribute:**

```typescript
// playwright.config.ts
export default defineConfig({
  use: {
    // Use custom attribute if data-testid conflicts with existing code
    testIdAttribute: 'data-pw',
  },
});

// component.tsx
<button data-pw="submit-button">Submit</button>

// test.spec.ts
await page.getByTestId('submit-button').click();
```

**When to use data-testid:**
- Dynamic content without stable text
- Multiple similar elements (list items, cards)
- Elements generated by third-party libraries
- Complex interactive components

Reference: [Playwright Test ID](https://playwright.dev/docs/locators#locate-by-test-id)
