---
id: ui-coverage/configuration/attributefilters
title: 'attributeFilters: control element identification'
description: >-
  Use attributeFilters to stop dynamic, auto-generated, or state-based
  attributes from splitting one element into many or mis-grouping elements in
  Cypress UI Coverage reports.
section: ui-coverage
source_path: docs/ui-coverage/configuration/attributefilters.mdx
version: 3163d68b20e695f2c76d40c85c3f3b956dd19a3b
updated_at: '2026-08-21T20:59:04.402Z'
---
# Ignore attributes for identification - `attributeFilters`

UI Coverage [identifies](/llm/markdown/ui-coverage/core-concepts/element-identification.md) and [groups](/llm/markdown/ui-coverage/core-concepts/element-grouping.md) elements from their attributes and their position in the DOM. Use `attributeFilters` to keep unstable attributes out of that process so your reports stay accurate.

To recognize the same element across [snapshots](/llm/markdown/ui-coverage/core-concepts/element-identification.md#Snapshots) and group similar elements together, Cypress builds an identifier for each element from its attributes and its position in the DOM. When an element carries attributes that change from render to render, such as auto-generated IDs, hashed class names, or state-based flags like `link--focused`, those unstable values leak into the identifier. The result is one real element reported as several, or distinct elements collapsed into the wrong group, which inflates your element counts and hides the coverage and accessibility findings you care about.

The `attributeFilters` configuration property tells Cypress which attributes and values it may **not** use for identification and grouping. Filter out the unstable attributes and Cypress falls back to the stable ones, so a single element stays a single element across snapshots and your reports deduplicate cleanly.

## Why use attributeFilters?

*   **Stop duplicate elements**: When an element's `id`, `class`, or other attribute is regenerated on each render, the same control is counted many times. Filtering the volatile attribute lets Cypress identify it consistently.
*   **Improve grouping accuracy**: Removing attributes that differ between otherwise-identical elements (for example a per-row `data-cy="user-123"`) lets Cypress group them as one repeated component.
*   **Ignore library-specific attributes**: Attributes injected by frameworks (Angular's `ng-*`, Emotion/JSS hashed classes, and similar) rarely describe an element's purpose and often add noise.

If instead you want Cypress to _prefer_ a particular attribute as an identifier when it is available, use [`significantAttributes`](/llm/markdown/ui-coverage/configuration/significantattributes.md). `attributeFilters` only ever removes attributes from consideration; it never promotes them.

## Built-in filters

Cypress already excludes the most common sources of unstable identifiers, such as auto-generated IDs and hashed CSS classes, so many applications need no `attributeFilters` configuration at all.

Your configured rules run **before** these built-in filters, so a rule with `include: true` can re-enable an attribute that a built-in filter would otherwise drop. Add `attributeFilters` when your application produces unstable identifiers that these defaults don't already cover.

## Scope

**Note:** setting `attributeFilters` at the root of your configuration impacts both UI Coverage and Cypress Accessibility reports. To configure the products separately, nest the property under a `uiCoverage` or `accessibility` key. A nested `attributeFilters` completely replaces a root-level one for that product; the two lists are not merged.

## Setting attributeFilters

To add or edit `attributeFilters`, open the **App Quality** tab in your project settings in Cypress Cloud. See [Setting configuration](/llm/markdown/ui-coverage/configuration/overview.md#Setting-configuration) for details, including how to regenerate past reports with a new configuration without rerunning your tests.

## Syntax

App Quality Config

```
{
  "attributeFilters": [
    {
      "attribute": string,
      "value": string,
      "include": boolean,
      "comment": string
    }
  ]
}
```

To scope rules to a single product, nest the property under a `uiCoverage` or `accessibility` key, as described in [Scope](#Scope) above.

### Options

| Option | Required | Default | Description |
| --- | --- | --- | --- |
| `attribute` | Required |  | A regular expression matched against attribute names. Must be a valid HTML attribute name. |
| `value` | Optional | `.*` | A regular expression matched against the attribute's value. |
| `include` | Optional | `true` | Whether the matched attribute may be used for identification (`true`) or is ignored (`false`). |
| `comment` | Optional |  | A note about why this rule exists, for your team's benefit. Comments appear only in the configuration itself. They have no effect on identification and are not displayed in reports. |

Each rule accepts only these four properties. Cypress Cloud validates your configuration and rejects any unrecognized property, as well as two rules that are exactly identical.

## How rules are applied

Whether Cypress may use an attribute to identify an element is decided by the **first** rule whose `attribute` and `value` both match. Because the first match wins, order your rules from most specific to least specific: put `include: true` exceptions first, then broader `include: false` catch-all rules. For example, you can exclude every `aria-label` from identification with a catch-all rule, while keeping a few known-stable values by listing `include: true` rules for them first.

An attribute that matches no rule is available for use, so `include: true` is only needed to define exceptions.

Keep these matching details in mind when writing patterns:

*   **Patterns match the whole string.** Each `attribute` and `value` pattern is anchored automatically, as if wrapped in `^(...)$`, so it must match the entire name or value. Use wildcards for partial matches: `value: "user-.*"` matches `user-123`, whereas `value: "user"` matches only the exact string `user`. If your pattern already starts with `^` or ends with `$`, it is used as written.
*   **Escape backslashes for JSON.** Because JSON uses the backslash as its own escape character, a regex token like `\d` must be written `\\d` in the configuration. A single backslash is dropped, turning `\d` into a literal `d` that matches nothing you intended.
*   **Attribute names are matched case-insensitively; values are matched case-sensitively.**
*   **Only attributes and individual `class` tokens can be filtered.** Rules never apply to an element's tag name or its position among siblings (`nth-child`), which Cypress may still fall back to when no attributes are usable.

## Examples

### Excluding framework-generated IDs

React's `useId` hook (used by libraries like MUI and Radix) produces `id` values such as `:r0:` that change on every render. Excluding them lets Cypress identify each field by its stable `name` instead.

App Quality Config

```
{
  "attributeFilters": [
    {
      "attribute": "id",
      "value": ":r.*",
      "include": false,
      "comment": "React useId() values are regenerated on every render"
    }
  ]
}
```

#### HTML

```
<form>
  <input id=":r0:" name="email" type="email" />
  <input id=":r1:" name="password" type="password" />
</form>
```

#### Element identifiers displayed

```
[name="email"]
[name="password"]
```

* * *

### Filtering an ID and the attributes that reference it

When you filter a dynamic `id`, also filter the attributes that point at it, or elements will still be identified by those related dynamic values. Here Angular Material generates `mat-*` IDs and wires them through `for` and `aria-describedby`. Common relationships to filter alongside an `id` include:

*   Form associations (`for` on labels)
*   ARIA relationships (`aria-labelledby`, `aria-describedby`, `aria-controls`, `aria-owns`, `aria-details`)

App Quality Config

```
{
  "attributeFilters": [
    {
      "attribute": "id|for|aria-describedby|aria-labelledby",
      "value": "mat-.*",
      "include": false,
      "comment": "Angular Material regenerates mat-* ids per render"
    }
  ]
}
```

#### HTML

```
<div>
  <label for="mat-input-0">Email</label>
  <input id="mat-input-0" name="email" aria-describedby="mat-hint-2" />
  <span id="mat-hint-2">We'll only use this to sign you in.</span>
</div>
```

#### Element identifiers displayed

```
label
[name="email"]
span
```

* * *

### Keeping one attribute while excluding a whole family

Because the first matching rule wins, an `include: true` rule listed before a broad `include: false` rule protects the attributes it matches. Here Cypress keeps identifying elements by your `data-cy` test hook while ignoring the stateful `data-*` attributes that component libraries add and change as the UI updates.

App Quality Config

```
{
  "attributeFilters": [
    {
      "attribute": "data-cy",
      "include": true,
      "comment": "Always identify elements by our test hook"
    },
    {
      "attribute": "data-.*",
      "include": false,
      "comment": "Ignore library state attributes like data-state, data-headlessui-state"
    }
  ]
}
```

#### HTML

```
<button data-cy="menu-toggle" data-headlessui-state="open" data-state="active">
  Menu
</button>
```

#### Element identifiers displayed

```
[data-cy="menu-toggle"]
```

* * *

### Grouping repeated elements by ignoring per-row IDs

Filtering a per-instance value lets Cypress group otherwise-identical elements into a single repeated component instead of tracking each row separately. Here each delete button carries the row's database ID, so removing that value collapses them into one group.

App Quality Config

```
{
  "attributeFilters": [
    {
      "attribute": "id",
      "value": "delete-user-\\d+",
      "include": false,
      "comment": "Row action ids include the per-user database id"
    }
  ]
}
```

#### HTML

```
<button id="delete-user-4821" class="delete-btn">Delete</button>
<button id="delete-user-9034" class="delete-btn">Delete</button>
```

#### Element identifiers displayed

```
.delete-btn (2 instances)
```

## Confirming a filter worked

You don't need to rerun your tests to check a rule. Regenerate a historical run from its **Properties** tab and the report is reprocessed with your current configuration. Confirm the change by checking that an element that previously appeared multiple times now appears once, or that a set of repeated elements collapsed into a single entry.

If nothing changed, revisit [How rules are applied](#How-rules-are-applied). The most common causes are a pattern that doesn't fully match the value, an earlier rule taking precedence, or a report that was processed before the configuration was saved.

## See also

*   [`significantAttributes`](/llm/markdown/ui-coverage/configuration/significantattributes.md): prioritize the attributes Cypress should prefer as identifiers. This is the complement to filtering out the ones it should ignore.
*   [`elements`](/llm/markdown/ui-coverage/configuration/elements.md): pin a specific element's identity when its attributes change between snapshots.
*   [`elementGroups`](/llm/markdown/ui-coverage/configuration/elementgroups.md): combine related elements into a single group.
*   [Element identification](/llm/markdown/ui-coverage/core-concepts/element-identification.md): how UI Coverage builds an identifier for each element.
*   [Configuration overview](/llm/markdown/ui-coverage/configuration/overview.md): where to set configuration and regenerate reports.
*   [UI Coverage FAQ](/llm/markdown/ui-coverage/faq.md): common questions and troubleshooting.
