What are the different browser contexts and when should I use them?
Browser contexts in Playwright are isolated environments that allow you to run multiple independent browser sessions within a single browser instance. Think of them as separate "profiles" or "sandboxes" that don't share cookies, local storage, or other browser state. Understanding when and how to use different browser contexts is crucial for efficient web scraping, testing, and automation.
Understanding Browser Contexts
A browser context is essentially a logical separation layer that creates an isolated browsing environment. Each context maintains its own:
- Cookies and session data
- Local storage and session storage
- Cache
- Permissions and geolocation settings
- Authentication state
- Network interceptors
This isolation makes browser contexts perfect for scenarios where you need to simulate different users, test various authentication states, or prevent data contamination between different operations.
Types of Browser Contexts
1. Default Browser Context
When you launch a browser without explicitly creating a context, Playwright automatically creates a default context. This is the simplest approach for basic automation tasks.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage(); // Uses default context
await page.goto('https://example.com');
await page.screenshot({ path: 'example.png' });
await browser.close();
})();
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page() # Uses default context
await page.goto('https://example.com')
await page.screenshot(path='example.png')
await browser.close()
asyncio.run(main())
2. Explicit Browser Contexts
Creating explicit contexts gives you more control over the browsing environment and allows you to configure specific settings for each context.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
// Create a new context with specific settings
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 },
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
locale: 'en-US',
timezoneId: 'America/New_York'
});
const page = await context.newPage();
await page.goto('https://example.com');
await context.close();
await browser.close();
})();
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
# Create a new context with specific settings
context = await browser.new_context(
viewport={'width': 1920, 'height': 1080},
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
locale='en-US',
timezone_id='America/New_York'
)
page = await context.new_page()
await page.goto('https://example.com')
await context.close()
await browser.close()
asyncio.run(main())
3. Incognito/Private Contexts
Every context you create in Playwright is essentially "incognito" by default, as they don't share state with other contexts. However, you can explicitly configure contexts to behave like private browsing sessions.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
// Create multiple isolated contexts
const context1 = await browser.newContext();
const context2 = await browser.newContext();
const page1 = await context1.newPage();
const page2 = await context2.newPage();
// These pages operate in complete isolation
await page1.goto('https://example.com');
await page2.goto('https://example.com');
// Login in context1 won't affect context2
await page1.fill('#username', 'user1');
await page1.fill('#password', 'pass1');
await page1.click('#login');
// page2 will still see the login page
await context1.close();
await context2.close();
await browser.close();
})();
When to Use Different Browser Contexts
1. Multi-User Testing
Use separate contexts when you need to simulate different users or test different authentication states simultaneously.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
// Admin user context
const adminContext = await browser.newContext();
const adminPage = await adminContext.newPage();
// Regular user context
const userContext = await browser.newContext();
const userPage = await userContext.newPage();
// Login as admin
await adminPage.goto('https://app.example.com/login');
await adminPage.fill('#username', 'admin');
await adminPage.fill('#password', 'admin123');
await adminPage.click('#login');
// Login as regular user
await userPage.goto('https://app.example.com/login');
await userPage.fill('#username', 'user');
await userPage.fill('#password', 'user123');
await userPage.click('#login');
// Test different permissions
await adminPage.goto('https://app.example.com/admin');
await userPage.goto('https://app.example.com/admin'); // Should be denied
await adminContext.close();
await userContext.close();
await browser.close();
})();
2. Geographic and Locale Testing
Create contexts with different geographic settings to test location-based features, similar to how you might handle browser sessions in Puppeteer for different user scenarios.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
// US context
const usContext = await browser.newContext({
locale: 'en-US',
timezoneId: 'America/New_York',
geolocation: { latitude: 40.7128, longitude: -74.0060 },
permissions: ['geolocation']
});
// UK context
const ukContext = await browser.newContext({
locale: 'en-GB',
timezoneId: 'Europe/London',
geolocation: { latitude: 51.5074, longitude: -0.1278 },
permissions: ['geolocation']
});
const usPage = await usContext.newPage();
const ukPage = await ukContext.newPage();
await usPage.goto('https://example.com');
await ukPage.goto('https://example.com');
// Pages will see different localizations and locations
await usContext.close();
await ukContext.close();
await browser.close();
})();
3. A/B Testing and Feature Flags
Use different contexts to test various configurations or feature flags without interference.
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
# Context A: Feature enabled
context_a = await browser.new_context(
extra_http_headers={'X-Feature-Flag': 'enabled'}
)
# Context B: Feature disabled
context_b = await browser.new_context(
extra_http_headers={'X-Feature-Flag': 'disabled'}
)
page_a = await context_a.new_page()
page_b = await context_b.new_page()
await page_a.goto('https://example.com')
await page_b.goto('https://example.com')
# Test different feature states
feature_a = await page_a.is_visible('#new-feature')
feature_b = await page_b.is_visible('#new-feature')
print(f"Feature visible in context A: {feature_a}")
print(f"Feature visible in context B: {feature_b}")
await context_a.close()
await context_b.close()
await browser.close()
asyncio.run(main())
4. Parallel Data Extraction
When scraping multiple pages or sites simultaneously, contexts prevent cookie and session conflicts, much like how you might run multiple pages in parallel with Puppeteer.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const urls = [
'https://site1.com',
'https://site2.com',
'https://site3.com'
];
// Create separate contexts for each site
const contexts = await Promise.all(
urls.map(() => browser.newContext())
);
const pages = await Promise.all(
contexts.map(context => context.newPage())
);
// Scrape all sites in parallel
const results = await Promise.all(
pages.map(async (page, index) => {
await page.goto(urls[index]);
return {
url: urls[index],
title: await page.title(),
content: await page.textContent('body')
};
})
);
console.log(results);
// Clean up
await Promise.all(contexts.map(context => context.close()));
await browser.close();
})();
Performance Considerations
Context Reuse
While contexts provide isolation, creating too many contexts can impact performance. Reuse contexts when possible:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const context = await browser.newContext();
// Reuse the same context for multiple pages
const page1 = await context.newPage();
const page2 = await context.newPage();
const page3 = await context.newPage();
// Navigate to different pages
await Promise.all([
page1.goto('https://example1.com'),
page2.goto('https://example2.com'),
page3.goto('https://example3.com')
]);
// Process pages...
await context.close();
await browser.close();
})();
Memory Management
Always close contexts when you're done with them to free up resources:
import asyncio
from playwright.async_api import async_playwright
async def scrape_with_context(browser, url):
context = await browser.new_context()
try:
page = await context.new_page()
await page.goto(url)
data = await page.text_content('body')
return data
finally:
await context.close() # Always close context
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
urls = ['https://site1.com', 'https://site2.com', 'https://site3.com']
# Process URLs with proper context management
results = await asyncio.gather(
*[scrape_with_context(browser, url) for url in urls]
)
await browser.close()
asyncio.run(main())
Best Practices
- Use explicit contexts for better control and configuration
- Create separate contexts for different users, locales, or test scenarios
- Reuse contexts when possible to improve performance
- Always close contexts to prevent memory leaks
- Configure contexts with appropriate settings for your use case
- Use context isolation to prevent data contamination between tests
Browser contexts are one of Playwright's most powerful features for creating robust, isolated automation scenarios. Whether you're testing multi-user applications, scraping data from multiple sources, or running parallel test suites, understanding how to effectively use browser contexts will significantly improve your automation workflows.