Aug 28, 2024

Understand Playwright's BrowserContexts and Pages

Understand Playwright's BrowserContexts and Pages

Paul Klein

Paul Klein

@pk_iv

Playwright (and Puppeteer) exposes its API through Browser, BrowserContext, and Page objects with a blurry definition of their boundaries. This article covers their definition, how to leverage them, and how to avoid common pitfalls.

What are BrowserContexts and Pages?

A typical Playwright program looks as follows:

import { chromium } from "playwright-core"; 

(async () => { 
  // 1. Get a Browser instance 
  const browser = await chromium.connectOverCDP( 
    `wss://connect.browserbase.com?apiKey=${process.env.BROWSERBASE_API_KEY}` 
  ); 
  
  // 2. Get a BrowserContext 
  const defaultContext = browser.contexts()[0]; 
  
  // 3. Get a Page 
  const page = defaultContext.pages()[0]; 
  
  // 4. Act on Page await 
  page.goto("<https://browserbase.com>"); 
  await page.close(); 
  await browser.close(); 
})().catch((error) => console.error(error.message));

Let's have a closer look at the Browser, BrowserContext, and Page definitions by comparing them to our day-to-day Browser:

Each concept accounts for a level of isolation and shared resources: BrowserContext gets a dedicated cache, later shared between Pages.

We can now visualize the relationship between them as follows:

Browser, BrowserContext, and Page are encapsulated concepts that enable developers to control different parts of the Browser.

Let’s now see how to leverage each concept when building automation.

How to use BrowserContexts and Pages

The most common use cases benefiting from using multiple BrowserContexts or Pages are parallelization and cookie management.

Leverage BrowserContexts for parallelization

Crawling a large number of web pages sequentially can be slow. A common practice is to create multiple pages and visit them in parallel to speed up the automation:

import { chromium } from "playwright-core"; 

(async () => { 
  const browser = await chromium.launch(); 
  
  const getContent = async (url: string) => { 
    const context = await browser.newContext(); 
    const page = await context.newPage(); 
    
    try { 
      await page.goto(url, { 
        waitUntil: "domcontentloaded", 
      }); 
      
      return await page.content(); 
    } finally { 
      await page.close(); 
    } 
  }; 
  
  await Promise.all( 
    ["<https://www.google.com>", "<https://www.bing.com>"].map(async (item) => { 
      const contents = []; 
      
      for (url in item.urls) { 
        contents.push(await getContent(url)); 
      } 
      
      return contents; 
    }), 
  ); 
  await browser.close(); 
})().catch((error) => console.error(error.message));

Using multiple BrowserContext is a good choice for parallelization as long as no authentication is involved.

However, remember that the overall performance of your automation’s parallelization will be limited by the underlying compute capacity assigned to the Browser.

Leverage Pages for cookie management

Pages sharing the same storage and cookies are a good fit for getting granular control of the navigation and history.

For example, a first page can be created to complete an authentication flow. In contrast, other pages can later be created to navigate other parts of a website with their dedicated history.

Let’s now see how Browserbase lets you reuse BrowserContext across multiple sessions to persist authentication.

BrowserContexts and Pages with Browserbase

Browserbase assigns each Browser instance (called a Browser Session) to a dedicated virtual machine (with isolated resources -- unlike your local browser) with a unique BrowserContext (by leveraging the Persistent Context API).

Combining isolated resources and a unique BrowserContext enables support for custom Chrome Extensions and guarantees better performance. You can use the Contexts API, to reuse BrowserContext across multiple sessions to handle authentication flows.

What will you 🅱️uild?

What will you 🅱️uild?

© 2024 Browserbase. All rights reserved.