cy.wait() is one of the most misused commands in Cypress. This lesson explains its two very different forms and when each one is (and is not) appropriate.
Two Very Different Uses of cy.wait()
cy.wait(number) pauses the test for a fixed number of milliseconds, no matter what. cy.wait('@alias') instead waits for a specific, previously aliased network request (defined via cy.intercept()) to actually occur, resolving as soon as it does.
These look similar but represent opposite philosophies: one guesses a duration, the other waits for a real, observable event. The alias form is almost always the correct choice.
// Avoid: guesses that 2 seconds is enough
cy.wait(2000);
// Prefer: waits for the actual network call to happen
cy.intercept('GET', '/api/products').as('getProducts');
cy.visit('/products');
cy.wait('@getProducts');
The alias-based wait resolves the instant the request completes, whether that takes 100ms or 3 seconds.
cy.wait() Syntax
cy.wait(milliseconds) // fixed delay, generally discouraged
cy.wait('@aliasName') // wait for one aliased request
cy.wait(['@alias1', '@alias2']) // wait for multiple aliased requests
The alias must be set up first with cy.intercept(...).as('aliasName').
Waiting on an array of aliases waits for all of them, in the order the requests actually complete.
cy.wait('@alias') yields the intercepted request/response object, useful for further assertions.
A fixed-number wait provides no information about what it is actually waiting for.
cy.wait() Cheat Sheet
When each form of cy.wait() is (or isn't) the right tool.
Form
Recommended?
Reason
cy.wait(2000)
Avoid
Guesses a duration; slow or flaky depending on load
cy.wait('@alias')
Recommended
Waits for a real, observable network event
cy.wait(['@a', '@b'])
Recommended
Waits for multiple related requests
Assertion-based wait
Recommended
Waits for a real DOM/state condition instead
Asserting on the Request cy.wait() Yields
cy.wait('@alias') yields an object describing the intercepted HTTP exchange, letting you assert on the request payload, headers, or the response body directly, useful for confirming your front-end actually sent what you expected.
A fixed-duration cy.wait() is occasionally acceptable for something with genuinely no observable signal, like waiting out a fixed client-side debounce delay with no corresponding UI change. Even then, it should be a deliberate, well-commented exception, not a default habit.
Reserve fixed waits for truly signal-less delays, and document why in a comment.
Keep any fixed wait as short as strictly necessary, not a generous "just in case" buffer.
Revisit fixed waits periodically, since an app change may later introduce a proper observable signal.
Common Mistakes
Defaulting to cy.wait(numberOfMs) whenever a test seems to need "a little more time."
Not aliasing network requests with cy.intercept().as() before trying to wait on them.
Forgetting that cy.wait('@alias') requires the intercept to be set up before the request actually fires.
Chaining many long fixed waits together, silently making the whole suite far slower without improving reliability.
Key Takeaways
cy.wait('@alias') waits for a real, aliased network event and should be strongly preferred.
cy.wait(number) guesses a fixed duration and should be avoided in almost all cases.
cy.wait('@alias') yields the intercepted request/response for further assertions.
Reserve fixed-duration waits for rare, genuinely signal-less scenarios, and keep them short and documented.
Pro Tip
Anytime you catch yourself writing cy.wait(someNumber), ask whether the thing you're actually waiting for is a network request, if so, alias it with cy.intercept() and switch to cy.wait('@alias') instead; it is almost always both faster and more reliable.
You now understand the two forms of cy.wait() and when to use each. Next, learn broader strategies for eliminating Flaky Tests.