πŸ’‘ If you like this website, please share it with your friends and network! πŸš€
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!