skip to content

When would you attach with browserType.connect rather than browserType.connectOverCDP?

level: seniorimportance: should knowfreq 38%

answer

  1. Both attach, neither launches
  2. Playwright protocol versus DevTools protocol
  3. One works for all three engines
  4. Lower fidelity is documented, not folklore
  5. A default context already exists

basics

~20 s

Use 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 s

Both 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 lines
typescript
import { 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

for a junior

Know that both calls attach to a browser running somewhere else rather than starting one, and that connectOverCDP only works with Chromium-based browsers.

for a middle

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.

for a senior

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.

for a principal

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