Core concepts
Browser, context and page
The three layers Playwright is built on — and why a new context is the cheapest clean slate you will ever get.
Browser works in three layers. Almost every question about isolation, speed, or "why is this test seeing the previous test's login" is answered by knowing which layer you are on.
One browser process, two signed-in users, three tabs. The two contexts share nothing — separate cookies, separate storage, separate permissions — which is the whole reason the layers are worth learning.
| Layer | Is | Costs | Opened with |
|---|---|---|---|
The browser
One process, one engine. Three are available, and between them they cover what people actually use:
| Engine | Ships in |
|---|---|
Playwright brings its own binaries, so there is no geckodriver, no chromedriver, and no driver version to keep in step with a browser version. The same three engines run on Windows, Linux and macOS.
New Browser starts headless unless you say otherwise — Open Browser, being a
debugging tool, starts headful:
The browser layer is where the environment is decided: which engine, headless or not, a proxy, a slow-motion delay, the browser's own timeout, extra launch arguments.
Browsers are reused
New Browser with the same arguments as an earlier New Browser call does not
start a second process — it switches to that one. That is what makes it safe to
call in a suite setup that runs many times. A browser that New Page or
New Context created implicitly is not a reuse candidate, so an explicit
New Browser after them does start a second process. To force a new one
deliberately:
The context
A context is an independent session inside an already-running browser: its own cookies, its own storage, its own permissions. Two contexts share nothing. The closest everyday equivalent is a fresh incognito window.
This is the layer worth understanding, because it is where Browser is structurally faster than the older tools. With tools that give you one session per browser process, an isolated session costs a process start. Here it is one call inside a process that is already warm:
So "log in as a different user" costs a few milliseconds of context creation rather than a browser start, and so does "start this test from a known-clean state". You do not clear cookies; you throw the context away and open another.
The context is also where the interesting configuration lives:
Downloads are accepted by default. Pass acceptDownloads=False if you want the
browser to refuse them.
The $user and $pwd above are not a typo. httpCredentials refuses a plain
value — it takes the $name placeholder form, and resolves the names from
variables, so the password never reaches the log.
Tracing and video recording are context-level too, which is why a trace covers exactly one session:
Persistent contexts
New Persistent Context is the exception to the shape: it takes a user data
directory and creates its own browser, so state survives between runs. Useful
when a real profile is the thing under test, and the wrong tool for isolation —
a browser opened this way cannot host additional contexts.
The page
A page is a tab. It holds the document and its own history, and it is where every selector resolves.
A popup, a target-blank link or a second tab is another page in the same
context, so it shares the login. It does not become the active page on its
own — reach it with Switch Page, given NEW, which returns the id of the page
you came from:
You rarely open all three
The layers fill themselves in downwards. New Page with nothing open starts a
browser and a context first, using defaults:
Open Browser does all three in one call. It is built for experiments and
debugging; when you care about the context, open the three yourself.
What is open right now
Every browser, context and page has an id, and Get Browser Catalog returns the
whole tree:
Drawn as a tree here for readability; the keyword returns a list of dictionaries, one per browser, each carrying its contexts and their pages.
The active* fields are the part worth reading, and they are not all the same
shape: activeBrowser is a boolean on the browser, while activeContext and
activePage hold ids — and every browser and context carries one, whether or
not it is the active branch. So activeBrowser is what picks the branch the
next keyword will act on.
This is the fastest way to answer "what does the library think is running" when
a suite has drifted from what you expected. Get Browser Ids, Get Context Ids
and Get Page Ids return the pieces individually.
Keywords act on the active browser, context and page unless you say otherwise,
and Switch Browser, Switch Context and Switch Page move that pointer.
When things close
By default, the contexts and pages a test opened are closed when the test
ends. Browsers are not: no auto-closing level closes a browser per test or per
suite, so a browser lives until execution ends or you call Close Browser. The
one exception is a browser opened by New Persistent Context — it is closed
together with its context.
That setting is auto_closing_level, and it has four values:
| Level | Contexts and pages are closed |
|---|---|
KEEP leaves processes running on purpose — it exists so you can poke at a page
after a failing test while writing it. Never set it in CI, or runs will leak
browser processes until the machine gives up.
Which layer for which job
| You want | Open |
|---|---|
The third row is the one that decides how long your suite takes. Reaching for a new browser where a new context would do is the single most common reason a Browser suite runs slower than it needs to.
In short
- Browser = process, context = session, page = tab.
- Contexts are the cheap unit of isolation. Use them freely.
New Pageopens whatever is missing underneath it.- Cookies, storage, permissions, viewport, locale, tracing and video all belong to the context.
auto_closing_level=KEEPis for writing tests, never for running them.