How to Configure Timeouts in Playwright
Learn which Playwright timeout controls a test, assertion, action, navigation, fixture, hook, or full run—and how to set the right limit without hiding flaky tests.
Configure the timeout for the scope that is actually slow. In Playwright Test, a test has a 30-second default budget, auto-retrying assertions have a separate 5-second default, browser actions and navigations have no timeout by default, and the full test run has no global limit by default. Set a shared default in playwright.config.ts, then use a narrower override when only one test or operation needs more time.
These values are Playwright defaults documented as of October 3, 2026. The example values below are starting points, not universal recommendations. [Playwright: Timeouts]
1. Set the timeouts you need in the config
Install Playwright Test in your project if it is not already installed, then create or update playwright.config.ts. This runnable configuration sets separate budgets for tests, retrying assertions, browser actions, navigation, and the overall run:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Budget for each test, including fixture setup and beforeEach.
timeout: 120_000,
// Separate budget for auto-retrying assertions.
expect: {
timeout: 10_000,
},
// Defaults for browser operations.
use: {
actionTimeout: 10_000,
navigationTimeout: 30_000,
},
// Optional hard cap for the entire test run.
globalTimeout: 3_600_000,
});
Run the suite with npx playwright test. Adjust each limit to the expected work and the environment where the suite runs. The relevant config properties are documented in the Playwright TestConfig API.
2. Know which timeout is responsible
| Scope | Config default | One-off override | Documented default |
|---|---|---|---|
One test, including fixture setup and beforeEach |
timeout |
test.setTimeout(ms) or test.slow() |
30,000 ms |
| Auto-retrying assertion | expect: { timeout: ms } |
Matcher option such as toBeVisible({ timeout: ms }) |
5,000 ms |
| Browser action | use.actionTimeout |
Action option such as locator.click({ timeout: ms }) |
No timeout |
| Navigation | use.navigationTimeout |
Navigation option such as page.goto(url, { timeout: ms }) |
No timeout |
| Entire test run | globalTimeout |
Run-level configuration | Disabled |
| Fixture setup | Fixture timeout option | Set the fixture’s own timeout | Shares the test timeout by default |
beforeAll and afterAll |
Hook timeout | Set timeout in the hook | Defaults to the test timeout |
Timeout scopes are distinct. Raising the assertion timeout does not raise the test timeout, and raising the test timeout does not set a timeout for a browser action. An operation may be constrained by the time remaining in its enclosing test even when that operation has no independent timeout configured. [Playwright: Timeouts]
3. Configure a single test, hook, or assertion
Give one test more time
Use test.setTimeout inside the test when just that test has a known slower workflow. test.slow() is shorthand that triples the test’s default timeout.
import { test, expect } from '@playwright/test';
test('imports a large data set', async ({ page }) => {
test.setTimeout(120_000);
await page.goto('https://example.com/import');
await page.getByRole('button', { name: 'Start import' }).click();
await expect(page.getByText('Import complete')).toBeVisible();
});
test('runs a known slow workflow', async ({ page }) => {
test.slow();
await page.goto('https://example.com/report');
await expect(page.getByRole('heading', { name: 'Report' })).toBeVisible();
});
Extend a test from a hook
From beforeEach, testInfo.setTimeout can extend the current test budget relative to the timeout already in effect:
import { test } from '@playwright/test';
test.beforeEach(async ({}, testInfo) => {
testInfo.setTimeout(testInfo.timeout + 30_000);
});
beforeAll and afterAll have their own timeout behavior. Set the hook timeout from within the hook when its shared setup or cleanup needs a different budget:
import { test } from '@playwright/test';
test.beforeAll(async ({}, testInfo) => {
testInfo.setTimeout(120_000);
// Perform shared setup.
});
See the Playwright Test API and TestInfo API for the timeout methods and hook context.
Give one assertion more time
Retrying assertions wait for the condition to become true up to their own timeout. Pass an option to one matcher when only that assertion needs more time:
await expect(page.getByRole('status'))
.toHaveText('Ready', { timeout: 15_000 });
For a suite-wide assertion budget, use expect: { timeout: 10_000 } in the config. Assertions and their retry behavior are covered in the Playwright assertions guide.
4. Configure action and navigation limits
Set shared browser operation defaults under use. An operation-level timeout overrides the default for that call:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
actionTimeout: 10_000,
navigationTimeout: 30_000,
},
});
import { test, expect } from '@playwright/test';
test('opens a page and submits a form', async ({ page }) => {
await page.goto('https://example.com/form', { timeout: 45_000 });
await page.getByLabel('Email').fill('dev@example.com', { timeout: 8_000 });
await page.getByRole('button', { name: 'Submit' }).click({ timeout: 8_000 });
await expect(page.getByRole('status')).toHaveText('Submitted');
});
The Page API also provides page- and browser-context-level default timeout methods. These can be useful when configuring defaults in setup code rather than through the test config. Consult the Page API for their exact scope and behavior.
5. Give a slow fixture its own budget
Fixtures share the test timeout by default. If fixture setup is the known slow part, assign the fixture its own timeout so the test body can retain a smaller budget. For example:
import { test as base } from '@playwright/test';
export const test = base.extend({
account: [async ({}, use) => {
const account = await createTestAccount();
await use(account);
await deleteTestAccount(account);
}, { timeout: 60_000 }],
});
This assumes your project defines createTestAccount and deleteTestAccount. The fixture’s timeout option is the relevant setting; it avoids increasing every test’s budget just to accommodate fixture setup. See the timeout guide for fixture timeout details.
6. Cap the complete test run
globalTimeout is a run-level limit. It is disabled by default. Configure it when a CI job needs a hard cap, for example:
import { defineConfig } from '@playwright/test';
export default defineConfig({
globalTimeout: 60 * 60 * 1_000,
});
This example caps a run at one hour. Choose a value that accounts for the suite’s expected workload and your CI environment. A global cap is useful when a broken setup could otherwise keep the run going too long; it does not replace the per-test or per-operation budgets. [Playwright TestConfig API]
7. Diagnose the timeout before increasing it
Use the timeout error and the failing line to identify the scope first. A test timeout points to the test’s total budget; a matcher timeout points to a condition that did not become true; an action or navigation timeout points to that operation’s own limit. Then check whether the expected condition is correct and whether the page reached the state the test needs.
Playwright cautions that a timeout alone may not explain flaky tests: “If you happen to be in this section because your tests are flaky, it is very likely that you should be looking for the solution elsewhere.” [Playwright: Timeouts]
For readiness, prefer an assertion about the user-visible condition the test depends on. The Page API discourages using networkidle as a testing readiness condition and recommends web assertions instead. [Playwright: Page API]
8. Common timeout errors and fixes
| Symptom | Likely cause | What to do |
|---|---|---|
Test timeout of 30000ms exceeded |
The test body, fixture setup, or beforeEach exceeded the test budget. |
Find the slow phase. Give a known slow test a larger budget, or assign slow fixture setup its own timeout. Check for a stalled navigation or condition that never occurs. |
An expect(...) matcher times out first |
The expected condition did not become true within the assertion budget, which defaults to 5 seconds. | Check the locator and expected state. If the state is valid but takes longer, increase the matcher timeout or suite assertion timeout. |
| A click, fill, or other action times out | The action’s configured or per-call limit expired, or the target was not ready for the action. | Check that the locator identifies the intended element and that the page state supports the action. Adjust actionTimeout or that call’s timeout if the operation legitimately needs longer. |
| A navigation does not finish | The navigation limit is too low for the expected page load, the URL is wrong, or the page never reaches the selected load condition. | Check the target URL and navigation condition. Increase navigationTimeout or the call timeout only for a known slow navigation; assert the page state the test actually needs. |
| CI stops a suite that passes locally | CI may have slower resources, or the run-level cap may be too low. | Inspect which scope expired and compare the run’s expected workload with the CI cap. Set a suitable globalTimeout and targeted per-test budgets. |
| Tests remain flaky after limits are raised | The test may rely on timing, an incorrect readiness signal, or unstable application behavior. | Replace arbitrary waiting with a retrying assertion for the success condition. Avoid networkidle as a general readiness signal in tests. |
9. Performance, reliability, and cost considerations
- Longer limits increase worst-case wait time. A test that is stuck can occupy a worker longer when its budget is larger. Keep routine test budgets close to expected durations and use an overall cap where the run needs one.
- Targeted limits make failures easier to interpret. Separate test, assertion, action, navigation, and fixture budgets help show which phase is slow. A single large timeout can conceal where the delay occurs.
- Timeouts do not make a condition reliable. A larger number only grants more time. Check the locator, readiness condition, and application behavior when failures are intermittent.
- CI runtime has a direct resource cost. The longer a stalled test or run is allowed to occupy CI workers, the longer those resources remain in use. A run cap limits the tail, while per-scope settings keep normal work appropriately bounded.
10. Capture a page screenshot for debugging
A screenshot can help inspect what the browser displayed when a test failed. Playwright Test supports taking a page screenshot directly:
import { test, expect } from '@playwright/test';
test('checkout shows confirmation', async ({ page }) => {
await page.goto('https://example.com/checkout');
try {
await expect(page.getByRole('heading', { name: 'Order confirmed' }))
.toBeVisible({ timeout: 10_000 });
} catch (error) {
await page.screenshot({ path: 'checkout-timeout.png', fullPage: true });
throw error;
}
});
This keeps the original assertion failure while saving a screenshot of the page for inspection. For browser setup, configuration, and capture options, use the Playwright documentation.
Or skip the browser setup
If you need a page image for debugging or documentation rather than a Playwright interaction, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API docs for the options and response details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Responses identify the page verdict and billing status in headers.
- An MCP server gives AI agents, including Claude, Cursor, and any MCP client, the
take_screenshot,get_page_info, andcapture_pdftools. - The Free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
FAQ
Can I set a timeout to zero?
Playwright documents the action and navigation defaults as having no timeout. For exact semantics of a particular API option, check that method’s API reference before changing it.
Does a longer timeout make a flaky test pass reliably?
It may allow genuinely slow work to finish, but it does not fix a bad locator, a wrong expected condition, or unstable behavior. Diagnose the failure scope and assert the state the test needs.
Should every test use the same timeout?
A shared default keeps ordinary tests consistent. Use targeted overrides for a known slow test, operation, hook, or fixture so the exception has a clear scope.


