Ignore attributes for identification - attributeFilters
UI Coverage identifies and groups 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 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. 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 for details, including how to regenerate past reports with a new configuration without rerunning your tests.
Syntax​
{
"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 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
attributeandvaluepattern is anchored automatically, as if wrapped in^(...)$, so it must match the entire name or value. Use wildcards for partial matches:value: "user-.*"matchesuser-123, whereasvalue: "user"matches only the exact stringuser. 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
\dmust be written\\din the configuration. A single backslash is dropped, turning\dinto a literaldthat matches nothing you intended. - Attribute names are matched case-insensitively; values are matched case-sensitively.
- Only attributes and individual
classtokens 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.
{
"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 (
foron labels) - ARIA relationships (
aria-labelledby,aria-describedby,aria-controls,aria-owns,aria-details)
{
"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.
{
"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.
{
"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. 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: prioritize the attributes Cypress should prefer as identifiers. This is the complement to filtering out the ones it should ignore.elements: pin a specific element's identity when its attributes change between snapshots.elementGroups: combine related elements into a single group.- Element identification: how UI Coverage builds an identifier for each element.
- Configuration overview: where to set configuration and regenerate reports.
- UI Coverage FAQ: common questions and troubleshooting.