Wait for an element to disappear

Let a spinner, loader, or toast finish before you assert on what replaces it.

A loading spinner blocks the content you want to check. Wait for the spinner to go, not for the clock to run out.

Assert that it is hidden

toBeHidden() polls until the element is gone or invisible, then returns. It is a web-first assertion, so it waits for the page state instead of a fixed delay.

php
use function Playwright\Testing\expect;

$page->getByRole('button', ['name' => 'Load report'])->click();

expect($page->getByRole('status'))->toBeHidden();
expect($page->getByRole('table'))->toBeVisible();

The spinner disappears, then the table assertion runs against a settled page.

Give a slow disappearance more room

The default timeout covers most cases. When a request is genuinely slow, raise the budget on that one assertion:

php
use function Playwright\Testing\expect;

expect($page->locator('.spinner'))->withTimeout(15000)->toBeHidden();

withTimeout() takes milliseconds. Keep the higher value local to the slow step rather than raising it for the whole run.

When you need the wait without an assertion

If you only want to pause until the node leaves the DOM, waitForSelector() with a hidden state does that and returns nothing to assert on:

php
$page->waitForSelector('.spinner', ['state' => 'hidden']);

Prefer the assertion form in tests. It documents intent and fails with a clear message when the element never leaves.

The sleep trap

php
sleep(2); // waits for the clock, not the page

sleep(2) is wrong twice over: it is too short when the server is slow, and wasted time when the server is fast. toBeHidden() returns the moment the element is gone and no sooner.

Expected result

The blocking element is gone or hidden, and the replacement content is visible. Keep both assertions when the disappearance matters only because it reveals the next state.

Go next

← All recipes