cypress iframe: how to interact with elements inside iframes
Shows how to test iframe contents in Cypress with cy.iframe() patterns. Use when the target element lives inside an iframe. Not for shadow DOM (separate skill).
TL;DR
Cypress commands do not pierce iframes by default. Get the iframe's body document first, then query inside it with cy.wrap(), and treat the iframe content as a separate scoped root for every command.
Error
CypressError: Timed out retrying after 4000ms: Expected to find element: `#pay-button`, but never found it.
(The button is inside a Stripe-style iframe.)Steps
- Confirm the element is inside an iframe using the runner's DOM snapshot. Expected: you see an
[iframe]ancestor. - Add the
cypress-iframeplugin or write the helper:cy.get('iframe').its('0.contentDocument.body').should('not.be.empty').then(cy.wrap). Expected: you hold the iframe body as a Cypress subject. - Query inside it:
cy.get('iframe').its('0.contentDocument.body').then(cy.wrap).find('#pay-button').click(). Expected: the click targets the inner element. - For repeated use, make it a custom command
cy.iframe()so tests read cleanly. Expected: one helper used across specs. - If the iframe is cross-origin, you also need
chromeWebSecurity: falsein config. Expected: Cypress can access the foreign document.
When to use
- Payment forms, embedded editors, or widgets rendered in iframes.
- The selector works in devtools console on the inner document but not via
cy.get().
When not to use
- Shadow DOM (different piercing mechanism).
- Same-page elements that are merely hidden (visibility skill).
Tool compatibility
- Cypress 10 through 14;
cypress-iframeplugin or a hand-rolled helper.
Variant phrasings
Cypress cannot find element inside iframe
The classic symptom; the fix is scoping commands to the iframe body.
contentDocument is null in Cypress iframe helper
The iframe had not loaded yet; add a .should() retry on the body before wrapping.
Why it happens
Each iframe has its own document. cy.get() queries the top document only, so inner elements are invisible to it.
Edge cases
chromeWebSecurity: falseweakens a security boundary; scope it to the specs that need it.- Iframes that lazy-load need a wait on their
loadevent, not a fixed sleep. - Nested iframes require piercing one level at a time.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst3X85-Dh79tCOerdYQvtyQ
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.