Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsOrganize contexts around the browser state that must be shared. In Playwright, keep related tabs and popups for one user in a single BrowserContext; create a separate context for each independent test, user identity, or isolation boundary. A context is the session, while a Page is a tab inside that session. WebDriver BiDi uses different terms, so apply its user-context model rather than assuming that a BiDi browsing context is equivalent to Playwright’s BrowserContext.
The three levels of browser state
Playwright has a simple hierarchy:
- Browser: the launched or connected browser process.
- BrowserContext: an isolated browser session with its own cookies, local storage, session storage, permissions and other session-level state.
- Page: a tab or document inside a context. Several pages can share one context.
A popup opened by a page remains in that page’s context, so it sees the same session state. Non-persistent contexts keep data in memory and do not write a browsing profile to disk.
The practical rule is: use a new context when state should be isolated; use another page when state should be shared.
Choose the boundary before writing the test
| Question | Same context | Separate context |
|---|---|---|
| Do the pages represent one signed-in user? | Yes, including related tabs and popups | No, create one per identity |
| Should cookies and storage carry over? | Yes | No |
| Is this an independent test? | Only when intentional shared state is part of the scenario | Normally, one clean context per test |
| Are two actors interacting, such as buyer and seller? | Only pages belonging to the same actor | One context for each actor |
| Must a profile survive browser restarts? | Use a deliberately managed persistent profile | Use separate user-data directories for separate profiles |
Playwright’s test runner creates an isolated context for each test by default. When using the library directly, your code owns context creation and shutdown, so make that lifecycle visible in fixtures or helper functions.
#1 Best Overall
Organize a single-user workflow
Keep pages together when they are parts of one user journey. For example, an account page may open a billing popup and a documentation tab. All three should be pages in one context so the login cookie and storage are available everywhere.
import { chromium } from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext();
try {
const account = await context.newPage();
await account.goto('https://example.test/account');
const popupPromise = context.waitForEvent('page');
await account.getByRole('button', { name: 'Open billing' }).click();
const billing = await popupPromise;
await billing.waitForLoadState();
const docs = await context.newPage();
await docs.goto('https://example.test/docs');
} finally {
await context.close();
await browser.close();
}
The popup is captured from the context because it belongs to that session. Closing the context closes its pages and gives Playwright a chance to flush artifacts before the browser closes.
Give every independent test a clean context
Tests should not depend on execution order. Reusing a context can leak cookies, local storage, permissions, service-worker state or visited-link state. Some browser state is difficult to remove reliably, so starting clean is safer than attempting a partial reset.
import { test, expect } from '@playwright/test';
test('customer sees an empty cart', async ({ page }) => {
await page.goto('https://shop.example.test/cart');
await expect(page.getByText('Your cart is empty')).toBeVisible();
});
test('customer can add a product', async ({ page }) => {
await page.goto('https://shop.example.test/products/widget');
await page.getByRole('button', { name: 'Add to cart' }).click();
await expect(page.getByTestId('cart-count')).toHaveText('1');
});
The runner supplies a page in a fresh context for each test. If you create contexts manually, use a fixture or try/finally block so every path closes it.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Model multiple users with multiple contexts
Multi-user tests are the main reason to create several contexts in one test. Put all pages for the administrator in one context and all pages for the customer in another. This prevents one actor’s cookies or storage from silently authenticating the other.
import { test, expect } from '@playwright/test';
test('admin approves a customer request', async ({ browser }) => {
const adminContext = await browser.newContext({ storageState: 'playwright/.auth/admin.json' });
const customerContext = await browser.newContext({ storageState: 'playwright/.auth/customer.json' });
try {
const admin = await adminContext.newPage();
const customer = await customerContext.newPage();
await customer.goto('https://app.example.test/request');
await customer.getByRole('button', { name: 'Submit' }).click();
await admin.goto('https://app.example.test/admin/requests');
await admin.getByRole('button', { name: 'Approve' }).click();
await customer.reload();
await expect(customer.getByText('Approved')).toBeVisible();
} finally {
await Promise.all([adminContext.close(), customerContext.close()]);
}
});
Keep contexts separate even when they run in the same browser process. The browser is a process-level container; the context is the isolation boundary your test should reason about.
Rank #2
Make authentication state explicit
Saved storage state is useful when logging in for every test would be slow or would trigger rate limits. Treat each state file as an input tied to one identity, not as a general-purpose shortcut.
import { chromium } from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext({
storageState: 'playwright/.auth/customer.json'
});
try {
const page = await context.newPage();
await page.goto('https://app.example.test/dashboard');
} finally {
await context.close();
await browser.close();
}
- Generate an admin state and a customer state separately.
- Store authentication files outside published artifacts and source control when they contain live credentials.
- Do not load an admin state into a customer scenario merely to avoid a login step.
- When a test needs two identities, initialize two contexts with their respective state files.
Persistent profiles need their own directories
A persistent context writes browser data to a user-data directory and is appropriate when an extension, profile setting or long-lived login must survive runs. Give automation a dedicated directory. Pointing Playwright at the everyday Chrome profile can prevent pages from loading or cause the browser to exit, and it risks corrupting or exposing personal browsing data.
Free tools Windows power users keep installed
One-click scans. No signup required.
import { chromium } from 'playwright';
const context = await chromium.launchPersistentContext('./automation-profile', {
headless: true
});
try {
const page = await context.newPage();
await page.goto('https://example.test');
} finally {
await context.close();
}
Do not launch two processes against the same profile directory at once. Use a different directory for each concurrent persistent worker and remove or archive directories according to your test-retention policy.
WebDriver BiDi: do not mix the terminology
WebDriver BiDi describes a browsing context as a navigable, such as a tab, iframe or popup. It also defines user contexts. Tabs in one user context share browser storage; tabs in different user contexts are isolated. Selenium’s BiDi APIs expose operations for opening tabs or windows, navigating, and inspecting the context tree.
That means a BiDi browsing context is not automatically the same thing as Playwright’s BrowserContext. Before designing fixtures, identify which abstraction your client library uses: a tab-level browsing context, a storage-sharing user context, or a library-specific session object.
Lifecycle, cleanup and parallel execution
- Create contexts as close as possible to the scenario that owns them.
- Close explicitly created contexts before closing the browser.
- Close pages only when you need to stop one tab early; closing the context is the complete cleanup operation.
- Keep test data and saved authentication state aligned with the context’s identity.
- When parallelizing or sharding, avoid shared mutable accounts unless the test deliberately models contention.
Playwright documents contexts as fast and cheap, but there is no universal safe concurrency number. CPU, memory, browser engine, page complexity, video or HAR recording, and the runner all change capacity. Measure your own workload and reduce workers when the host starts swapping, navigation time rises, or the application begins throttling requests.
Rank #3
Useful organization patterns
One test, one user
Use the runner’s supplied page, or create one context and several pages for a tabbed workflow. This is the default for most functional tests.
One test, several users
Create one context per actor, optionally loading a distinct storage state into each. Never simulate isolation by opening two pages in one context.
Independent API and UI setup
Use API calls or fixtures to prepare data, then create the UI context with only the authentication state required by the test. This keeps setup concerns separate from browser state.
Long-lived monitoring session
Use a persistent context only when persistence is the requirement. Otherwise prefer a fresh non-persistent context so stale state cannot hide regressions.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Troubleshooting context problems
A test is unexpectedly logged in
Cause: a context or persistent profile was reused. Fix: create a new context, remove unintended storageState, and verify the user-data directory is not shared.
A second tab cannot see the first tab’s session
Cause: the tabs were created in different contexts. Fix: create both with context.newPage(), or deliberately transfer the required authentication state into the new context.
Rank #4
Admin actions appear as the customer
Cause: both actors share one context or the wrong state file was loaded. Fix: use separate contexts and log the identity associated with each state file.
Runs become flaky after several tests
Cause: leaked contexts, pages or persistent profiles. Fix: close contexts in teardown, make cleanup run after failures, and give parallel persistent workers separate directories.
Recommended Free Tools
The browser exits or pages fail to load with a profile
Cause: automation is using the regular Chrome user-data directory or a directory already locked by another process. Fix: create a dedicated directory and run one owner per directory.
Parallel runs overload the machine
Cause: the workload exceeds the host’s practical capacity; no universal context-per-worker limit exists. Fix: measure navigation time and memory under your real pages, then tune worker count and artifact recording.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
When the goal is a clean image or PDF rather than an interactive test, ScreenshotNeo provides a one-call website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result.
With a free API key, run:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the complete parameter reference in the ScreenshotNeo documentation. The same endpoint supports PNG, JPEG or WebP, PDF output, full-page captures with lazy images loaded, CSS-selector element shots, dark mode, device presets, arbitrary viewports, retina scale, custom CSS and JavaScript, clicks before capture, hidden selectors, selector/delay/network-idle waits, request and resource blocking, custom headers/cookies/user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, usage reporting and an OpenAPI specification. Parameter names used by other screenshot APIs also work, easing migrations.
Python:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
An MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients, so an AI agent can perform captures without you maintaining browser fixtures.
Best Value
| Plan | Included shots | Price |
|---|---|---|
| Free | 1,000/month | $0, no card |
| Starter | 3,000 | $5 |
| Growth | 15,000 | $15 |
| Pro | 60,000 | $39 |
| Scale | 250,000 | $99 |
| Business | 1,000,000 | $249 |
Yearly billing provides two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to get 1,000 screenshots a month without a card.
Decision checklist
- Define whether cookies and storage should be shared.
- Map each human or service identity to one context.
- Put same-user tabs and popups in that context.
- Use a fresh context for each independent test unless shared state is intentional.
- Assign saved authentication state to the identity it represents.
- Use dedicated directories for persistent profiles.
- Close contexts before browsers and tune concurrency from measurements.
- For BiDi, distinguish browsing contexts from user contexts before designing fixtures.
Frequently Asked Questions
Can several pages in one Playwright context use different accounts?
Not safely. Pages in one context share session storage and cookies, so use separate contexts for separate accounts.
Does closing a page clear its cookies?
No. Cookies belong to the context. Close the context when the session itself should end.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Should I use persistent contexts for every test?
No. Use non-persistent contexts for isolated tests; reserve persistent contexts for requirements such as extensions or state that must survive restarts.
Is a WebDriver BiDi browsing context the same as a Playwright BrowserContext?
No. BiDi’s browsing context is a navigable such as a tab, iframe or popup, while BiDi user contexts define storage sharing.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

