Back to All Questions
Question 15 of 100
Mechanics
Intermediate
How do you handle waiting / synchronization issues in Playwright?
The Answer
Use the hierarchy: (1) Auto-waiting (built-in, free). (2) Web-First assertions (`expect().toBeVisible()`). (3) Explicit waits (`waitFor`, `waitForResponse`). Never use `waitForTimeout` (sleep).
Deep Dive Explanation
The mental model: Playwright should always wait for SOMETHING SPECIFIC (element state, network call, URL change), never for an arbitrary amount of time. If you're reaching for `waitForTimeout`, ask yourself 'what condition am I actually waiting for?' and wait for that condition instead.
example.spec.ts
// β
Tier 1: Auto-waiting (automatic for all actions)
await page.getByRole('button', { name: 'Load' }).click(); // Waits for button
// β
Tier 2: Web-First assertion (retries until true)
await expect(page.locator('.results')).toBeVisible({ timeout: 10000 });
// β
Tier 3: Wait for specific network response
await page.waitForResponse(res =>
res.url().includes('/api/data') && res.status() === 200
);
// β
Tier 3: Wait for URL change after navigation
await page.waitForURL('**/dashboard');
// β
Tier 3: Wait for element state change
await page.locator('#spinner').waitFor({ state: 'hidden' });
// β NEVER: Hard sleep (unreliable, slow)
await page.waitForTimeout(3000); // Don't do this!