When would you attach with browserType.connect rather than browserType.connectOverCDP?
answer
- Both attach, neither launches
- Playwright protocol versus DevTools protocol
- One works for all three engines
- Lower fidelity is documented, not folklore
- A default context already exists
basics
~20 sUse browserType.connect whenever the far side is a Playwright browser server: it speaks Playwright's own protocol, works for all three engines, and keeps full fidelity. connectOverCDP is the Chromium-only, lower-fidelity way to attach to a browser someone else started.
solid answer
~40 sBoth attach to a browser running elsewhere instead of starting one locally. `browserType.connect(wsEndpoint)` speaks Playwright's own protocol to a Playwright browser server, so the remote engine is patched and behaves exactly like a local one; it works for Chromium, Firefox and WebKit and carries options such as `timeout`, `slowMo`, `headers` and `exposeNetwork`. `browserType.connectOverCDP(endpointURL)` speaks the Chrome DevTools Protocol to a Chromium-based browser started with a remote-debugging port, and the documentation calls it significantly lower fidelity. Its distinguishing feature is that state already exists: `browser.contexts()[0]` is the default context and its first page is the open one. So prefer `connect` for anything Playwright owns, and reach for `connectOverCDP` only when the browser is not yours to start.
code
typescript · 12 linesimport { chromium } from 'playwright';
const remote = await chromium.connect('ws://booking-runner:5678/', {
exposeNetwork: '<loopback>',
timeout: 30_000,
});
const page = await (await remote.newContext()).newPage();
await page.goto('http://localhost:3000/rooms/deluxe-suite');
const attached = await chromium.connectOverCDP('http://localhost:9222');
const existing = attached.contexts()[0].pages()[0];
await existing.reload();go deeper
Know that both calls attach to a browser running somewhere else rather than starting one, and that connectOverCDP only works with Chromium-based browsers.
Explain the protocol difference and its consequences: Playwright's own protocol across all three engines with full fidelity, versus DevTools Protocol into an already-running Chromium with existing state.
Argue the choice from failure modes. Every action now crosses a socket, a dropped connection surfaces as a closed target rather than an assertion failure, and reduced fidelity shows up as features quietly not working.
Own the question of when attaching is warranted at all, who owns the lifetime of the far-side process, and what a suite loses by putting a network hop under every interaction.
## Two different wires Both calls attach Playwright to a browser **that is already running somewhere else**, rather than starting one locally. They are not interchangeable. `browserType.connect(wsEndpoint)` speaks **Playwright's own protocol** to a Playwright browser server — one created by `browserType.launchServer()` in Node, whose address comes from `browserServer.wsEndpoint()`. The remote side is a Playwright-patched engine, so what you get back behaves exactly like a locally started browser. `browserType.connectOverCDP(endpointURL)` speaks the **Chrome DevTools Protocol** to a browser that was started by someone else with a remote-debugging port open, typically at `http://localhost:9222`. The documentation is blunt about the tradeoff: this connection is significantly lower fidelity than the Playwright protocol, and if you hit trouble or need advanced functionality you probably want `connect`. ## How they differ in practice | | `browserType.connect` | `browserType.connectOverCDP` | |---|---|---| | Protocol | Playwright's own | Chrome DevTools Protocol | | Engines | Chromium, Firefox and WebKit | Chromium-based only | | Far side | a Playwright browser server | any browser with debugging open | | Fidelity | full | reduced | | Contexts | you create them | a default context already exists | - With `connectOverCDP` the browser already has state: `browser.contexts()[0]` is the default context and `pages()[0]` the page that is open, so you attach to an existing session rather than building one. That is the whole point when the target is a browser a person or another process started. - With `connect` the remote engine was launched by Playwright with its curated argument list. A browser you started by hand for CDP was not, and missing those arguments is a documented way for parts of Playwright to misbehave once attached. - `connect` carries connection options of its own — `timeout`, `slowMo`, `headers`, and `exposeNetwork`, which makes hostnames reachable from the client visible to the remote browser. That last one is what lets a remote engine reach a hotel-booking build running on your laptop. - Neither call downloads an engine. The build lives wherever the far side is; on the client you need the Playwright package but not necessarily any installed browser. ## Choosing between them 1. **Reach for `connect`** whenever the far side is a Playwright endpoint. It is the only one that works for Firefox and WebKit, and the only one that gives you the full feature surface. 2. **Reach for `connectOverCDP`** when the browser is *not* Playwright's to start: a Chromium already running with a debugging port, a session another tool owns, a long-lived profile you must not restart. You accept reduced fidelity to get access to something that already exists. 3. **Reach for neither** in an ordinary suite. Attaching to a remote engine buys you nothing on a machine that can run its own, and it adds a network hop to every action. ## The cost of attaching at all Every action, assertion and network interception now crosses a socket. On a slow or distant link the booking suite gets slower and gains a new failure mode: the connection itself can drop mid-test, and that surfaces as a target-closed style error rather than a clean assertion failure. Closing a browser you connected to disconnects your client; whether the far-side process dies with it depends on who owns that process, which is exactly the sort of thing to establish before wiring a suite to it.
- Why does the first context behave differently between the two calls?`connectOverCDP` attaches to a browser that is already running with state, so `browser.contexts()[0]` is its default context and `pages()[0]` the page already open. With `connect` you are talking to a Playwright browser server and create contexts yourself, exactly as you would locally.
- What breaks when you attach over CDP to a browser you launched by hand?Playwright normally starts Chromium with a curated argument list. A browser started without those arguments can misbehave once attached — parts of the feature surface depend on them. Combined with the lower fidelity of the protocol, that makes hand-launched CDP targets a debugging tool rather than a suite foundation.
One is a phone call to a colleague who speaks your language; the other is shouting through a doorway in a language that carries only part of what you mean.
saying these in an interview costs you the question
- Treats the two calls as interchangeable
- Expects connectOverCDP to work with Firefox or WebKit
- Thinks connecting downloads an engine to the client
- Assumes full feature parity over a CDP attachment
- Calls newContext then wonders why the open page is missing