Browser → Context → Page

Understand the three most important Playwright objects and learn how to use Browser, BrowserContext and Page in real-world automation projects.

Beginner → Real-Time Examples → Multi-User → Professional
🚀 1. Browser, Context and Page — The Big Picture

The Most Important Playwright Relationship

Playwright automation is easier to understand when you visualize the hierarchy: Browser → BrowserContext → Page. A browser represents the browser process, a context provides an isolated browser session, and a page represents a tab within that context.

🌐 Browser Browser process
→
🔐 BrowserContext Isolated session
→
📄 Page Browser tab
🌐

Browser

Controls the browser process, such as Chromium, Firefox or WebKit.

🔐

BrowserContext

Provides an isolated session with its own cookies, local storage and session state.

📄

Page

Represents a tab where you navigate, locate elements, interact and validate the UI.

🟢 2. Beginner Example — Launch Browser

Start with the simplest possible Playwright example: launch a browser and create a page.

browser_example.py
from playwright.sync_api import sync_playwright


with sync_playwright() as p:

    browser = p.chromium.launch(
        headless=False
    )

    page = browser.new_page()

    page.goto("https://example.com")

    print(page.title())

    browser.close()
Playwright
→
Browser
→
Page
→
Website
🌐 3. Understanding the Browser Object

The Browser object represents the browser process launched by Playwright.

Launch Different Browsers
from playwright.sync_api import sync_playwright


with sync_playwright() as p:

    chromium = p.chromium.launch()

    firefox = p.firefox.launch()

    webkit = p.webkit.launch()

    chromium.close()

    firefox.close()

    webkit.close()
Important:

You normally do not need to launch all three browsers inside one test. A test suite can be configured to execute scenarios against different browser engines.

🔐 4. Understanding BrowserContext

A BrowserContext is an isolated browser session. Think of it like a fresh browser profile created inside the same browser process.

Create Browser Context
from playwright.sync_api import sync_playwright


with sync_playwright() as p:

    browser = p.chromium.launch()

    context = browser.new_context()

    page = context.new_page()

    page.goto("https://example.com")

    print(page.title())

    context.close()

    browser.close()
Key idea:

The context owns the session state, while the page is where your web interactions happen.

📄 5. Understanding Page

A Page represents a browser tab. Most day-to-day Playwright automation happens through the Page object.

Page Operations
page.goto("https://example.com")

print(page.url)

print(page.title())

page.reload()

page.go_back()

page.go_forward()

page.screenshot(
    path="homepage.png"
)
Page
→
goto()
→
Locate
→
Action
→
Assertion
📑 6. Multiple Pages Inside One Context

One BrowserContext can contain multiple Page objects. This is useful when your application opens multiple tabs.

Multiple Tabs
browser = p.chromium.launch()

context = browser.new_context()

page1 = context.new_page()

page2 = context.new_page()

page1.goto("https://example.com")

page2.goto("https://example.org")

print(page1.title())

print(page2.title())

context.close()

browser.close()
🌐 Browser
→
🔐 Context
→
📄 Page 1
+
📄 Page 2
🔗 7. Real-Time Example — New Tab

Suppose clicking a button opens a new tab. Playwright can wait for the new Page and then interact with it.

new_tab.py
with page.expect_popup() as popup_info:

    page.get_by_role(
        "link",
        name="Open Report"
    ).click()


new_page = popup_info.value

new_page.wait_for_load_state()

print(new_page.title())

new_page.close()
Real-world scenario:

This pattern can be useful for payment pages, reports, external authentication pages, documentation links or other workflows that open a separate tab.

👥 8. Real-Time Example — Two Independent Users

One of the most useful BrowserContext features is session isolation. You can create separate contexts for different users.

Admin Context
↔
Application
↔
Customer Context
admin_customer.py
from playwright.sync_api import sync_playwright


with sync_playwright() as p:

    browser = p.chromium.launch(
        headless=False
    )


    # Admin session
    admin_context = browser.new_context()

    admin_page = admin_context.new_page()


    # Customer session
    customer_context = browser.new_context()

    customer_page = customer_context.new_page()


    admin_page.goto(
        "https://your-app.example/admin"
    )

    customer_page.goto(
        "https://your-app.example"
    )


    # Each context has separate session state.


    admin_context.close()

    customer_context.close()

    browser.close()
Why this matters:

The admin and customer sessions can maintain different cookies, storage and authentication state without requiring two separate browser processes.

🍪 9. BrowserContext and Cookies

Cookies belong to the browser context. This makes contexts useful for testing separate authenticated sessions.

cookies.py
context = browser.new_context()

page = context.new_page()

page.goto(
    "https://your-app.example"
)

cookies = context.cookies()

print(cookies)

context.add_cookies([
    {
        "name": "test_cookie",
        "value": "12345",
        "domain": "your-app.example",
        "path": "/"
    }
])
Remember:

When you create a completely new BrowserContext, it starts with independent session state unless you explicitly provide state such as saved authentication information.

💾 10. BrowserContext and Local Storage

BrowserContext isolation also helps when applications store client-side data such as local storage.

local_storage.py
context = browser.new_context()

page = context.new_page()

page.goto(
    "https://your-app.example"
)

page.evaluate("""
    localStorage.setItem(
        'userRole',
        'admin'
    )
""")

value = page.evaluate("""
    localStorage.getItem('userRole')
""")

print(value)
🔑 11. Real-Time Example — Authentication State

You can save authentication state from a context and reuse it when creating another context.

save_auth.py
context = browser.new_context()

page = context.new_page()

page.goto(
    "https://your-app.example/login"
)

page.get_by_label(
    "Username"
).fill("testuser")

page.get_by_label(
    "Password"
).fill("Password123")

page.get_by_role(
    "button",
    name="Login"
).click()

page.wait_for_url(
    "**/dashboard"
)

context.storage_state(
    path="auth.json"
)

context.close()

Reuse the Authentication State

reuse_auth.py
context = browser.new_context(
    storage_state="auth.json"
)

page = context.new_page()

page.goto(
    "https://your-app.example/dashboard"
)
Professional use:

Authentication state can reduce repeated login setup when many tests need an already authenticated session.

⚙️ 12. BrowserContext Configuration

A context can be configured with options that describe the environment your test should simulate.

context_options.py
context = browser.new_context(

    viewport={
        "width": 1280,
        "height": 720
    },

    locale="en-IN",

    timezone_id="Asia/Kolkata",

    color_scheme="light",

    user_agent="Playwright-Test-Agent"

)

page = context.new_page()

page.goto(
    "https://your-app.example"
)

📐 Viewport

Test the application at a specific browser viewport size.

🌍 Locale

Simulate a specific locale for localization testing.

🕐 Timezone

Test date and time behavior using a selected timezone.

🎨 Color Scheme

Test light or dark color-scheme behavior.

📱 13. Real-Time Example — Mobile Device Context

Playwright can create browser contexts with device characteristics from its device descriptors.

mobile_context.py
from playwright.sync_api import sync_playwright


with sync_playwright() as p:

    browser = p.chromium.launch()

    mobile = p.devices["iPhone 13"]

    context = browser.new_context(
        **mobile
    )

    page = context.new_page()

    page.goto(
        "https://your-app.example"
    )

    print(page.title())

    context.close()

    browser.close()
Real-time scenario:

A QA team can use device contexts to check responsive layouts and mobile-oriented browser behavior without physically changing the development machine.

🏢 14. Professional Real-Time Example — Admin Approves Customer Request

Consider a business application where a customer submits a request and an administrator approves it.

Customer Login
→
Submit Request
→
Admin Login
→
Approve Request
→
Customer Verifies
admin_customer_workflow.py
def test_customer_admin_workflow(browser):

    # -----------------------------
    # Customer Context
    # -----------------------------

    customer_context = browser.new_context()

    customer_page = customer_context.new_page()

    customer_page.goto(
        "https://your-app.example/login"
    )

    customer_page.get_by_label(
        "Username"
    ).fill("customer")

    customer_page.get_by_label(
        "Password"
    ).fill("CustomerPass"
    )

    customer_page.get_by_role(
        "button",
        name="Login"
    ).click()


    # Customer submits request

    customer_page.get_by_role(
        "button",
        name="New Request"
    ).click()

    customer_page.get_by_label(
        "Request"
    ).fill("Leave Request")

    customer_page.get_by_role(
        "button",
        name="Submit"
    ).click()


    # -----------------------------
    # Admin Context
    # -----------------------------

    admin_context = browser.new_context()

    admin_page = admin_context.new_page()

    admin_page.goto(
        "https://your-app.example/login"
    )

    admin_page.get_by_label(
        "Username"
    ).fill("admin")

    admin_page.get_by_label(
        "Password"
    ).fill("AdminPass"
    )

    admin_page.get_by_role(
        "button",
        name="Login"
    ).click()


    # Admin approves request

    admin_page.get_by_text(
        "Leave Request"
    ).click()

    admin_page.get_by_role(
        "button",
        name="Approve"
    ).click()


    # -----------------------------
    # Customer verifies
    # -----------------------------

    customer_page.reload()

    customer_page.get_by_text(
        "Approved"
    ).is_visible()


    customer_context.close()

    admin_context.close()
Test design note:

For production test suites, credentials should be supplied securely rather than stored directly in the test source.

📊 15. Browser vs Context vs Page
Object Represents Main Responsibility Typical Methods
Browser Browser process Launch and manage browser new_context(), close()
BrowserContext Isolated session Cookies, storage, session configuration new_page(), cookies(), storage_state()
Page Browser tab Navigation and UI interaction goto(), click(), fill(), screenshot()
🔀 16. Multiple Contexts vs Multiple Pages

📄 Multiple Pages

Use multiple pages when you need multiple tabs sharing the same browser session.

context = browser.new_context()

page1 = context.new_page()

page2 = context.new_page()

🔐 Multiple Contexts

Use multiple contexts when you need independent browser sessions.

context1 = browser.new_context()

context2 = browser.new_context()
Easy rule:

New tab? Think Page.
New independent user session? Think BrowserContext.

📸 17. Context + Page Screenshot

Screenshots belong to the Page because the Page represents the visible browser tab.

screenshot.py
context = browser.new_context(
    viewport={
        "width": 1440,
        "height": 900
    }
)

page = context.new_page()

page.goto(
    "https://your-app.example"
)

page.screenshot(
    path="homepage.png",
    full_page=True
)

context.close()
⭐ 18. Professional Best Practices
✓ Reuse a browser process where appropriate
✓ Use isolated contexts for independent sessions
✓ Use Page objects for tab-level interaction
✓ Avoid unnecessary browser launches
✓ Keep authentication state controlled
✓ Close contexts after custom workflows
✓ Use fixtures for reusable setup
✓ Keep user sessions isolated
✓ Use pages for multi-tab workflows
✓ Capture screenshots from the relevant page
🏆 19. Complete Browser → Context → Page Example
complete_example.py
from playwright.sync_api import (
    sync_playwright,
    expect
)


def test_browser_context_page():

    with sync_playwright() as p:

        # =================================
        # 1. Browser
        # =================================

        browser = p.chromium.launch(
            headless=False
        )


        # =================================
        # 2. Browser Context
        # =================================

        context = browser.new_context(
            viewport={
                "width": 1280,
                "height": 720
            }
        )


        # =================================
        # 3. Page
        # =================================

        page = context.new_page()


        # =================================
        # 4. Navigate
        # =================================

        page.goto(
            "https://example.com"
        )


        # =================================
        # 5. Validate
        # =================================

        expect(page).to_have_title(
            "Example Domain"
        )


        # =================================
        # 6. Screenshot
        # =================================

        page.screenshot(
            path="example.png"
        )


        # =================================
        # 7. Cleanup
        # =================================

        context.close()

        browser.close()
Complete mental model:

Browser starts the browser process → Context creates an isolated session → Page represents the tab → Page methods perform navigation and UI actions → Assertions validate the result.

🎯 20. Final Learning Path
Browser
→
Context
→
Page
→
Locator
→
Action
→
Assertion

Remember This Rule

Browser = the browser process.
BrowserContext = an isolated browser session.
Page = a tab inside that session.

Next Recommended Lesson:

Playwright Locators & Selectors — learn get_by_role(), get_by_label(), get_by_text(), get_by_placeholder(), CSS selectors, XPath, locator chaining and stable locator strategies.