cypress-dynamic-wait-react
v2.0.0
Published
A Cypress plugin that auto-waits for non-blank, hydrated React pages before each command.
Maintainers
Readme
📦 cypress-dynamic-wait-react
A lightweight, safe, and production-ready Cypress plugin that adds dynamic waiting for React apps by enhancing:
cy.get(selector)cy.contains(text)cy.contains(selector, text)
The plugin helps Cypress tests remain stable on React apps that:
- Render content asynchronously
- Use hydration (Next.js, Remix)
- Use lazy-loaded components
- Display skeleton screens or transitions
- Start with a blank DOM before mounting
Unlike global command patches, this plugin does not overwrite click/type/then, ensuring zero Cypress queue interference and no hangs.
🚀 Features
✅ Dynamic wait for React app hydration
Waits automatically when the page is blank—but only for cy.get and cy.contains, not globally.
✅ Zero hangs
All internal waits are bounded and guaranteed to resolve.
✅ Cypress-safe
Does not patch or interfere with:
cy.thency.wrapcy.visitclick/type/etc.
✅ Supports both contains modes
cy.contains("Login")cy.contains("button", "Submit")
✅ Works with all major React architectures
- Next.js
- CRA
- Vite React
- React Router
- Component hydration
- Suspense/lazy
📥 Installation
npm install cypress-dynamic-wait-reactor
yarn add cypress-dynamic-wait-react⚙️ Setup
Add the plugin in cypress/support/e2e.js (or for TypeScript: cypress/support/e2e.ts):
import {
registerDynamicGetWait,
registerDynamicContainsWait
} from "cypress-dynamic-wait-react";
registerDynamicGetWait({
timeoutMs: 10000, // max wait before giving up
pollIntervalMs: 50, // how frequently to check for non-blank page
rootSelectors: ["#root", "#app"], // your React root
debug: false // set true for logs
});
registerDynamicContainsWait({
timeoutMs: 10000,
pollIntervalMs: 50,
rootSelectors: ["#root", "#app"],
debug: false
});That's it — no further code changes needed.
🧪 Usage Example
Your tests don't change. The plugin runs automatically.
describe("Login flow", () => {
it("should login successfully", () => {
cy.visit("/login");
cy.contains("Email").type("[email protected]");
cy.contains("Password").type("password123");
cy.contains("Sign In").click();
cy.get("#dashboard").should("be.visible");
});
});What the plugin does internally:
- Detects blank or empty React roots
- Waits for first meaningful DOM content
- Then executes the normal Cypress command
- No explicit "wait" needed
⚡ Why This Plugin Is Safe
A major cause of Cypress flakiness is overwriting commands like click or type, or wrapping promises around Cypress commands—this breaks Cypress's internal command queue and leads to hangs.
This plugin avoids all of that.
✔ Does NOT overwrite:
thenwrap- action commands (
click/type/etc.) - query commands
✔ Only patches cy.get and cy.contains
Which are the two commands that actually search the DOM.
✔ Promise always resolves
Bounded retries, guaranteed resolution.
✔ Zero recursion or infinite loops
This plugin is intentionally conservative to ensure maximum stability.
🔍 Debug Logging
Enable debug logs to see plugin behavior:
registerDynamicGetWait({ debug: true });
registerDynamicContainsWait({ debug: true });Example output:
[dynamic-wait] get("#nav-Data") waiting for non-blank page
[dynamic-wait] Page non-blank detected
[dynamic-wait] contains("Login") found immediately⚙️ Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| timeoutMs | number | 8000 | Maximum wait time before giving up |
| pollIntervalMs | number | 50 | How often to check for non-blank |
| rootSelectors | string[] | ["#root", "#app"] | React mount points |
| debug | boolean | false | Logging for troubleshooting |
🛑 What This Plugin Does NOT Do
To avoid Cypress instability, the plugin intentionally does NOT:
- Patch
cy.click,cy.type, etc. - Patch
thenorwrap - Modify Cypress's internal queue
- Add global waits before every command
- Attempt to detect "React done rendering" (impossible reliably)
The design is precise and minimalistic: 👉 only wait when querying the DOM.
❓ FAQ
Does this replace cy.wait()?
No — it supplements dynamic waits only for DOM queries.
Will it slow down tests?
Only when the page is actually blank. If elements exist, there’s zero delay.
Is it compatible with Cypress 13+?
Yes — no deprecated APIs used.
Does it work in Cypress Component Testing?
Yes.
Does it work with Angular/Vue?
Yes, though optimized for React root detection.
🧩 Roadmap
- Optional support for
cy.find - Integration with intelligent hydration detection
- Vue and Angular root profiles
- Plugin config validation
