BLOG
Playwright Browser Extension: What It Is and What to Use Instead
A Playwright browser extension isn't what most developers think. Learn what Playwright actually installs, how browser extensions fit into automation, and when a remote browser is the better path.
# Playwright Browser Extension: What It Is and What to Use Instead
If you searched for a "Playwright browser extension," you probably want one of two things: a Chrome extension that lets Playwright drive your existing browser, or a way to load your own extension into a Playwright-controlled session. Neither exists as a single installable product. Playwright is a Node/Python/Java/.NET library that launches or connects to browsers over the Chrome DevTools Protocol (CDP) — it is not distributed as a browser extension, and no official Playwright extension ships on the Chrome Web Store.
This guide explains what Playwright actually installs, how browser extensions interact with Playwright automation, and when a hosted remote browser removes the extension problem entirely.
What Playwright installs (and what it doesn't)
When you run npx playwright install, you get browser binaries — Chromium, Firefox, and WebKit — downloaded to a local cache directory. You do not get a Chrome extension. The playwright npm package is a client library plus a driver; it talks to browsers over CDP or Playwright's own protocol.
The confusion usually comes from three places:
- Playwright's browser contexts look like profiles, so people assume there's an extension model.
- Chrome extensions can automate pages via
chrome.debugger, which overlaps conceptually with CDP. - Third-party "Playwright extensions" exist on GitHub — community projects that wrap Playwright in a DevTools panel or a Chrome extension UI. They are not maintained by Microsoft and are not part of the Playwright release.
If you want a browser extension that *controls* Playwright, you're building an unusual architecture. If you want Playwright to *load* a browser extension, that's supported — but only for Chromium, and only with specific launch options.
Loading a browser extension into Playwright
Playwright supports loading unpacked Chromium extensions via launchPersistentContext. The extension must be an unpacked directory (not a .crx), and you must use a persistent context because extensions need a real profile.
import { chromium, BrowserContext } from 'playwright';
async function launchWithExtension(): Promise<BrowserContext> {
const context = await chromium.launchPersistentContext('./user-data', {
headless: false, // extensions require headed mode in most Chromium builds
args: [
'--disable-extensions-except=./my-extension',
'--load-extension=./my-extension',
],
});
const page = await context.newPage();
await page.goto('https://example.com');
return context;
}Key constraints:
- Chromium only. Firefox and WebKit do not support the
--load-extensionflag through Playwright. - Headed mode required in most configurations. New headless Chromium has partial extension support, but it is not reliable across versions.
- Persistent context required.
browser.newContext()cannot load extensions because it creates an ephemeral profile. - No Chrome Web Store installs. You must download and unpack the extension yourself.
For CI, this is awkward. Headed Chromium needs a display server (Xvfb on Linux), the extension directory has to be vendored into your repo, and version drift between your extension and the Chromium build Playwright ships will break things silently.
Playwright browser launch options that matter
The launch and launchPersistentContext options are where most extension and CDP behavior is configured. The ones that come up repeatedly:
| Option | Purpose | Notes |
|---|---|---|
headless | Run without a display | Extensions usually need false |
args | Chromium command-line flags | Where --load-extension goes |
channel | Use installed Chrome/Edge instead of bundled Chromium | chrome, msedge, chrome-beta |
executablePath | Point at a specific browser binary | Overrides channel |
userDataDir | Persistent profile directory | Required for extensions |
proxy | Route traffic through a proxy | Per-context, not per-page |
ignoreDefaultArgs | Drop Playwright's default flags | Use sparingly; breaks CDP assumptions |
If you're connecting to a remote browser instead of launching one, most of these options don't apply — you pass a CDP endpoint to chromium.connectOverCDP() and the remote runtime owns the launch flags. That's covered in the Playwright CDP guide.
Playwright supported browsers and the extension gap
Playwright officially supports Chromium, Firefox, and WebKit. Extension support is a Chromium-only feature, and even there it's limited to unpacked extensions loaded at launch.
| Browser | Playwright support | Extension loading | CDP support |
|---|---|---|---|
| Chromium | Full | Yes (unpacked, headed) | Yes |
| Chrome (channel) | Full | Yes (unpacked, headed) | Yes |
| Edge (channel) | Full | Yes (unpacked, headed) | Yes |
| Firefox | Full | No | Partial (via connect_over_cdp in recent versions) |
| WebKit | Full | No | No |
This table is the reason most teams that need extensions end up on Chromium. It's also the reason "Playwright Firefox extension" searches return nothing useful — the capability doesn't exist.
Installing and managing Playwright browsers
npx playwright install downloads browsers to ~/.cache/ms-playwright (Linux/macOS) or %USERPROFILE%\AppData\Local\ms-playwright (Windows). To install manually:
# Install a specific browser
npx playwright install chromium
# Install with system dependencies (Linux CI)
npx playwright install --with-deps chromium
# Force re-download
npx playwright install --force chromiumTo uninstall Playwright browsers, delete the cache directory or run npx playwright uninstall. This removes the binaries but not the npm package.
The operational problem: every CI runner, every developer laptop, and every container needs the right browser version matched to the Playwright version. Version skew between playwright and the installed Chromium is the single most common source of "works on my machine" failures. A hosted runtime sidesteps this by pinning the browser version server-side — you connect over CDP and never install a binary. See Remote Browser online for how that connection works.
Playwright MCP and the Chrome extension question
The Playwright MCP server is a separate project from the Playwright library. It exposes browser automation as MCP tools so an LLM client (Claude Desktop, Cursor, etc.) can drive a browser. It does not ship as a Chrome extension, and it does not connect to your existing Chrome tab by default — it launches its own browser.
If you want an MCP client to control a browser you already have open, you need a CDP endpoint. That means either:
- Launching Chrome with
--remote-debugging-port=9222and pointing the MCP server athttp://localhost:9222. - Connecting to a hosted browser that exposes a CDP URL.
The second option is what Remote Browser provides. You get a wss:// CDP endpoint, pass it to connectOverCDP, and your MCP client or Playwright script drives a real Chromium instance without any local install.
When a remote browser beats a local extension setup
The extension path makes sense when:
- You're testing an extension you're developing.
- You need the extension's UI in a headed browser you can watch.
- Your workload is small and runs on a machine you control.
A remote browser makes more sense when:
- You need many concurrent sessions and don't want to manage Xvfb, display servers, or browser binaries.
- You want persistent profiles that survive across runs without vendoring
userDataDirdirectories. - You need proxy configuration, session isolation, or a live viewer for debugging.
- You're running AI agents that need a stable CDP endpoint rather than a local Chrome that might crash.
The trade-off is real: a remote browser can't load your unpacked extension unless the runtime supports it. If extension loading is a hard requirement, you're on local Chromium. If it isn't, the remote path removes a category of infrastructure work.
For a deeper comparison of the two architectures, see Remote Browser for AI agents.
Connecting Playwright to a remote browser over CDP
Here's the concrete pattern. You get a CDP WebSocket URL from your browser provider, then connect:
import { chromium, Browser, Page } from 'playwright';
async function connectToRemote(cdpUrl: string): Promise<void> {
const browser: Browser = await chromium.connectOverCDP(cdpUrl);
// Reuse the existing context — remote browsers usually start with one
const context = browser.contexts()[0] ?? await browser.newContext();
const page: Page = context.pages()[0] ?? await context.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
console.log(await page.title());
// Do NOT call browser.close() unless you want to kill the remote session.
// Disconnect instead:
await browser.close();
}
connectToRemote(process.env.CDP_URL!).catch(console.error);Two things to note:
connectOverCDPis Chromium-only. Firefox and WebKit cannot be driven this way. The Playwright CDP documentation confirms this.browser.close()on a CDP connection terminates the remote browser. If you want to keep the session alive for a later reconnect, you need to disconnect without closing — behavior varies by provider, so check your runtime's docs.
The CDP protocol itself is documented at chromedevtools.github.io/devtools-protocol if you need to drop below the Playwright abstraction.
Production criteria for extension-free automation
If you're moving away from local extension setups, evaluate a remote runtime on these axes:
- CDP endpoint stability. Does the WebSocket URL survive reconnects? Can you resume a session after a network blip?
- Profile persistence. Are cookies, localStorage, and IndexedDB preserved across sessions, or does every run start clean?
- Proxy and network controls. Can you set per-session proxies, and are they applied at the browser level or the context level?
- Session isolation. Are concurrent sessions truly isolated, or do they share a profile directory?
- Observability. Is there a live viewer, a session log, or a way to inspect a running page without attaching a debugger?
- Browser version pinning. Does the provider pin Chromium versions, or do you get whatever's latest and hope your selectors still work?
Remote Browser exposes these as configurable browser settings — persistent profiles, session isolation, proxy configuration, and a live viewer — rather than as flags you pass at launch. Current limits and pricing are on the /pricing page.
What to do if you actually need an extension
If extension loading is non-negotiable, your options are:
- Local Chromium with `launchPersistentContext`. Works, but you own the display server, the profile directory, and the browser version.
- Self-hosted Chromium in a container. You can bake the extension into the image and expose CDP. This is the most flexible path but requires you to run the infrastructure.
- A hosted runtime that supports extension loading. Some providers allow custom Chromium builds or extension injection. Check before assuming — most don't.
For everything else — scraping, testing, AI agent workflows, form automation — the extension is a distraction. You don't need it, and the infrastructure cost of maintaining it usually exceeds the value.
Summary
There is no official Playwright browser extension. Playwright is a library that launches or connects to browsers; extensions are a Chromium feature you can load via launchPersistentContext with --load-extension, and only in headed mode. If you're searching for a "Playwright Chrome extension install," you're probably looking for either the Playwright MCP server (which is not an extension) or a way to connect Playwright to an existing browser (which is CDP, not an extension).
For production automation, the extension question usually dissolves once you move to a hosted runtime. You connect over CDP, skip the browser install entirely, and get persistent profiles and session isolation without vendoring userDataDir directories. Start with the documentation to see the connection flow, or read Remote web browser for the broader architecture.