## TL;DR

The element existed when Cypress found it but the framework re-rendered and replaced it before the action ran. Fix by re-querying inside the action chain, waiting for the UI to settle, or asserting on the final state instead of the intermediate element.

## Error

```text
CypressError: `cy.click()` failed because this element is detached from the DOM.
`[button class="x1"]Save[/button]` has been removed from the DOM, but a reference to it was retained.
```

## Steps

1. Identify what re-renders: look for a state update, list refresh, or animation between the `cy.get()` and the failing action. Expected: you find the component that replaces the node.
2. Chain the action directly off a fresh query instead of aliasing the element: `cy.get('[data-cy=save]').click()` rather than storing the element in a variable. Expected: Cypress re-queries right before clicking.
3. If a list refreshes after an API call, wait on the network first: `cy.intercept('POST', '/api/save').as('save'); cy.get('[data-cy=save]').click(); cy.wait('@save')`. Expected: the click happens before the refresh it triggers.
4. For animations, assert the settled state first: `cy.get('[data-cy=panel]').should('be.visible')` before clicking inside it. Expected: Cypress waits for the assertion to pass, which implies rendering finished.
5. As a last resort, retry the action with a should-retry wrapper, not a fixed `cy.wait()`. Expected: flakiness drops without adding dead time to every run.

## When to use

- The error names `detached from the DOM`.
- The same test passes when run slowly (headed mode) and fails headless.

## When not to use

- The element never appears at all (selector or timing issue).
- You are testing React/Vue component re-mount logic itself (then the detachment is the behavior under test).

## Tool compatibility

- Cypress 10 through 14; common with React, Vue, and Svelte apps that re-render aggressively.

## Variant phrasings

### cy.type() failed because this element is detached from the DOM

Same cause: input replaced between focus and typing, often by validation re-renders.

### element has been removed from the DOM but a reference was retained

The long form of the same error; the fix is identical.

## Why it happens

Cypress holds a reference to a DOM node. Modern frameworks routinely destroy and recreate nodes on state changes. If that happens between query and action, the reference points at a node no longer in the document.

## Edge cases

- Aliasing with `.as()` then `cy.get('@alias')` re-queries, which is safe; storing the raw element in a JS variable is not.
- Detachment inside `.each()` loops over lists is common; re-query the list each iteration.
- Third-party widgets (date pickers, rich text editors) replace their DOM constantly; interact via their API or keyboard rather than clicking inner nodes.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst__x9ZAxpQP2Gfl7Veu6Ci7g
