Overview
Playwright is built on a client-server architecture where your test code (client) communicates with browser instances (server) through a protocol-based messaging system. This design enables Playwright to control browsers remotely and provide powerful automation capabilities.
Core Components
Client Layer
The client layer is what you interact with in your test code. It consists of several key classes:
Browser - Represents a browser instance (client/browser.ts:31)
BrowserContext - Isolated browser session (client/browserContext.ts:58)
Page - Individual tab or page (client/page.ts:80)
Frame - Frame within a page (client/frame.ts:48)
Locator - Element selector and action wrapper (client/locator.ts:40)
Server Layer
The server layer runs in a separate process and controls the actual browser:
BrowserType - Browser launcher and manager (server/browserType.ts:48)
BrowserServer - Browser process manager
Browser-specific implementations - Chromium, Firefox, and WebKit adapters
Communication Protocol
Playwright uses a channel-based protocol for client-server communication:
// From client/browser.ts:31
export class Browser extends ChannelOwner < channels . BrowserChannel > {
readonly _contexts = new Set < BrowserContext >();
private _isConnected = true ;
constructor ( parent : ChannelOwner , type : string , guid : string , initializer : channels . BrowserInitializer ) {
super ( parent , type , guid , initializer );
this . _channel . on ( 'context' , ({ context }) => this . _didCreateContext ( BrowserContext . from ( context )));
this . _channel . on ( 'close' , () => this . _didClose ());
}
}
Each object has:
GUID - Unique identifier for remote object
Channel - Message pipe for method calls and events
Initializer - Initial state from server
Object Hierarchy
Playwright
Root object that provides browser launchers
BrowserType
Provides methods to launch or connect to browsers (chromium, firefox, webkit)
Browser
Browser instance with multiple contexts
BrowserContext
Isolated session with independent cookies, storage, and cache
Page
Individual tab within a context
Frame
Main frame or iframe within a page
Context Creation Flow
When you create a new browser context, here’s what happens internally:
// From client/browser.ts:58-90
async newContext ( options : BrowserContextOptions = {}): Promise < BrowserContext > {
// 1. Prepare context options with selectors
const options = this . _browserType . _playwright . selectors . _withSelectorOptions ( userOptions );
// 2. Run before-create instrumentation
await this._instrumentation.runBeforeCreateBrowserContext(options);
// 3. Send create command to server
const contextOptions = await prepareBrowserContextParams ( this . _platform , options );
const response = await this . _channel . newContext ( contextOptions );
// 4. Get context proxy object
const context = BrowserContext . from ( response . context );
// 5. Run after-create instrumentation
await this._instrumentation.runAfterCreateBrowserContext(context);
return context;
}
The _channel object handles serialization, network transport, and deserialization automatically.
Event System
Playwright uses event emitters for asynchronous notifications:
// From client/browserContext.ts:102-112
this . _channel . on ( 'page' , ({ page }) => this . _onPage ( Page . from ( page )));
this . _channel . on ( 'route' , ({ route }) => this . _onRoute ( network . Route . from ( route )));
this . _channel . on ( 'serviceWorker' , ({ worker }) => {
const serviceWorker = Worker . from ( worker );
serviceWorker . _context = this ;
this . _serviceWorkers . add ( serviceWorker );
this . emit ( Events . BrowserContext . ServiceWorker , serviceWorker );
});
Events bubble up the hierarchy:
Frame events → Page events → Context events
Network events are emitted at both Page and Context levels
Resource Management
Playwright implements proper cleanup:
// From client/browser.ts:176-189
async close ( options : { reason? : string } = {}): Promise < void > {
this. _closeReason = options . reason ;
try {
if ( this . _shouldCloseConnectionOnClose )
this . _connection . close ();
else
await this . _channel . close ( options );
await this . _closedPromise ;
} catch (e) {
if ( isTargetClosedError ( e ))
return;
throw e;
}
}
Always close browsers, contexts, and pages to free resources. Use await browser.close() or async disposal.
Connection Types
Playwright supports multiple connection mechanisms:
Local Launch
const browser = await chromium . launch ();
Playwright launches and controls a local browser process.
Remote Connection
const browser = await chromium . connect ( 'ws://remote-browser:8080' );
Connect to a browser running elsewhere (useful for distributed testing).
Persistent Context
const context = await chromium . launchPersistentContext ( './user-data' );
Launch with saved user data (cookies, storage, etc.).
Context Reuse - Contexts are cheaper than browsers. Reuse browsers across tests.
Parallel Execution - Multiple contexts can run in parallel within one browser.
Connection Pooling - The channel protocol is optimized for low latency.
Event Subscription - Events are only sent when listeners are attached (client/browserContext.ts:166-173).
Implementation Details
Channel Ownership
From client/channelOwner.ts, every Playwright object extends ChannelOwner:
export class ChannelOwner < T > {
readonly _connection : Connection ;
readonly _channel : T ;
readonly _guid : string ;
_wrapApiCall < R >( func : () => Promise < R >, options : WrapOptions ) : Promise < R > {
// Wraps every API call with instrumentation, logging, and error handling
}
}
This provides:
Automatic error conversion
Stack trace preservation
Logging and tracing
Instrumentation hooks
Browser-Specific Implementations
Each browser has its own server-side implementation:
Chromium - Uses Chrome DevTools Protocol (CDP)
Firefox - Uses Juggler protocol
WebKit - Uses WebKit Inspector Protocol
The client API remains the same across all browsers, providing true cross-browser compatibility.
Next Steps
Browsers Learn about browser types and launching
Browser Contexts Understanding isolated browser sessions
Pages and Frames Working with pages and iframes
Selectors Element selection strategies