Playwright Page Object Model – Complete Beginner Guide with TypeScript Examples & Enterprise Framework (2026 Guide)

Introduction: Why Page Object Model Is Important in Automation Testing

As automation projects grow, maintaining test scripts becomes increasingly difficult. Imagine writing the same locators and actions in dozens of test files. If the application’s UI changes, you must update every test individually, making the framework hard to maintain.

This is where the Playwright Page Object Model (POM) becomes essential.

The Playwright Page Object Model is one of the most widely used design patterns in automation testing. It helps organize your code by separating page interactions from test logic. Instead of writing locators and actions inside every test, you place them in dedicated page classes that can be reused across multiple test cases.

Whether you are:

Learning the Playwright Page Object Model is a must for building scalable, maintainable, and enterprise-ready automation frameworks.

In this guide, you’ll learn:

Let’s begin.


What Is the Playwright Page Object Model?

The Playwright Page Object Model (POM) is a design pattern where each web page is represented by a separate TypeScript class.

Instead of placing locators and actions inside test files, they are stored inside page classes.

Simple Definition

The Playwright Page Object Model is a framework design pattern that separates page interactions from test logic, improving code reuse, readability, and maintainability.

For example:

Without POM:

Login Test

Find Username

Find Password

Click Login

Every test repeats these steps.

With POM:

Login Test

LoginPage.login()

Dashboard

The login logic is written once and reused everywhere.


Why Use the Playwright Page Object Model?

Without the Playwright Page Object Model:

  • Duplicate locators
  • Duplicate actions
  • Difficult maintenance
  • Large test files
  • Poor scalability

With the Playwright Page Object Model:

  • Reusable page classes
  • Centralized locators
  • Cleaner tests
  • Easier maintenance
  • Better framework design

Benefits of Using the Page Object Model

The Playwright Page Object Model offers several advantages.

1. Code Reuse

Write login functionality once and reuse it across hundreds of tests.


2. Easier Maintenance

If the Login button changes, update only the page class.


3. Better Readability

Tests become easier to understand because they focus on business scenarios instead of implementation details.


4. Scalability

Large enterprise projects often contain hundreds of pages. POM keeps them organized.


5. Separation of Concerns

  • Page classes → UI interactions
  • Test files → Business validation

Playwright POM Project Structure

A recommended enterprise project structure is:

playwright-framework/

├── pages/

│     ├── LoginPage.ts

│     ├── DashboardPage.ts

│     └── ProductsPage.ts

├── tests/

│     ├── login.spec.ts

│     ├── dashboard.spec.ts

│     └── orders.spec.ts

├── fixtures/

│     ├── auth.fixture.ts

├── utils/

│     ├── helpers.ts

├── data/

│     ├── users.json

├── playwright.config.ts

└── package.json

This structure keeps automation projects modular and maintainable.


Creating Your First Page Object

Create a new file:

pages/LoginPage.ts

Example:

import { Page } from ‘@playwright/test’;

export class LoginPage {

    constructor(private page: Page) {}

    async navigate() {

        await this.page.goto(‘https://example.com/login’);

    }

    async login(username: string, password: string) {

        await this.page.getByLabel(‘Username’).fill(username);

        await this.page.getByLabel(‘Password’).fill(password);

        await this.page.getByRole(‘button’, {

            name: ‘Login’

        }).click();

    }

}


Constructor Explained

The constructor receives the Playwright Page object.

constructor(private page: Page) {}

This makes the page available throughout the class without passing it to every method.


Locators Explained

Instead of locating elements inside every test:

await page.getByLabel(‘Username’);

Place locators inside page methods.

Advantages:

  • Easier updates
  • Better readability
  • Reusable actions

Creating Reusable Page Methods

Instead of:

await page.fill(‘#username’, ‘admin’);

await page.fill(‘#password’, ‘admin123’);

await page.click(‘#login’);

Create:

async login(username:string,password:string){

   …

}

Now every test simply calls:

await loginPage.login(‘admin’,’admin123′);


Writing Tests Using Page Objects

Example:

import { test, expect } from ‘@playwright/test’;

import { LoginPage } from ‘../pages/LoginPage’;

test(‘Login Test’, async ({ page }) => {

    const loginPage = new LoginPage(page);

    await loginPage.navigate();

    await loginPage.login(‘admin’, ‘admin123’);

    await expect(page).toHaveURL(/dashboard/);

});

Notice how the test is much shorter and easier to understand.


Real-World Playwright Page Object Model Example (TypeScript)

LoginPage.ts

import { Page } from ‘@playwright/test’;

export class LoginPage {

    constructor(private page: Page) {}

    async open() {

        await this.page.goto(‘https://example.com/login’);

    }

    async login(user: string, pass: string) {

        await this.page.getByLabel(‘Username’).fill(user);

        await this.page.getByLabel(‘Password’).fill(pass);

        await this.page.getByRole(‘button’, {

            name: ‘Login’

        }).click();

    }

}

login.spec.ts

import { test, expect } from ‘@playwright/test’;

import { LoginPage } from ‘../pages/LoginPage’;

test(‘Successful Login’, async ({ page }) => {

    const loginPage = new LoginPage(page);

    await loginPage.open();

    await loginPage.login(‘admin’, ‘admin123’);

    await expect(page).toHaveURL(/dashboard/);

    await expect(

        page.getByRole(‘heading’, { name: ‘Dashboard’ })

    ).toBeVisible();

});

This example demonstrates how to keep page interactions inside the page class while the test focuses only on validating the application’s behavior.


POM Workflow Diagram

Playwright Test

        │

        ▼

Page Object

        │

        ▼

Playwright Browser

        │

        ▼

Web Application

        │

        ▼

Response

        │

        ▼

Assertions

The Page Object acts as a reusable layer between the test and the application.

Reusable Components in Playwright Page Object Model

As your automation framework grows, you’ll notice that many UI elements appear on multiple pages.

Examples include:

  • Navigation menu
  • Header
  • Footer
  • Search bar
  • User profile menu
  • Notification panel

Instead of duplicating their locators across multiple Page Objects, create reusable component classes.

For example:

pages/

    LoginPage.ts

    DashboardPage.ts

components/

    HeaderComponent.ts

    SidebarComponent.ts

    FooterComponent.ts

Header Component Example

import { Page } from ‘@playwright/test’;

export class HeaderComponent {

    constructor(private page: Page) {}

    async search(product: string) {

        await this.page.getByPlaceholder(‘Search’)

            .fill(product);

        await this.page.keyboard.press(‘Enter’);

    }

    async logout() {

        await this.page.getByRole(‘button’, {

            name: ‘Logout’

        }).click();

    }

}

Now every page can reuse this component.

Example:

const header = new HeaderComponent(page);

await header.search(“Laptop”);

This is a common enterprise framework design.


Enterprise Framework Example

A scalable Playwright framework generally follows this structure:

playwright-framework/

├── pages/

│      LoginPage.ts

│      DashboardPage.ts

│      ProductPage.ts

│      CheckoutPage.ts

├── components/

│      HeaderComponent.ts

│      SidebarComponent.ts

│      MenuComponent.ts

├── fixtures/

│      auth.fixture.ts

│      api.fixture.ts

├── tests/

│      login.spec.ts

│      order.spec.ts

│      payment.spec.ts

├── utils/

│      logger.ts

│      config.ts

│      helpers.ts

├── data/

│      users.json

│      products.json

├── reports/

├── playwright.config.ts

└── package.json

This framework separates:

making maintenance much easier.


How Playwright POM Improves Automation

Without POMWith Playwright POM
Duplicate locatorsCentralized locators
Duplicate login codeReusable methods
Difficult maintenanceEasy updates
Long test filesClean test files
Hard to scaleEnterprise-ready
Poor readabilityBetter readability

Best Practices for Playwright Page Object Model

1. One Class Per Page

Each page should have its own class.

Example:

LoginPage

DashboardPage

OrdersPage

ProductsPage

Avoid putting multiple pages in one class.


2. Keep Assertions Inside Tests

Page Objects should perform actions.

Example:

await loginPage.login();

Tests should verify results.

Example:

await expect(page)

    .toHaveURL(/dashboard/);

This keeps responsibilities separate.


3. Use Meaningful Method Names

Good:

login()

logout()

searchProduct()

placeOrder()

Avoid:

clickButton()

button1()

method2()

Descriptive methods improve readability.


4. Prefer Playwright Locators

Recommended:

getByRole()

getByLabel()

getByPlaceholder()

getByText()

Avoid long XPath expressions whenever possible.


5. Keep Page Objects Small

A LoginPage should only contain login-related functionality.

Don’t place:

  • Product methods
  • Payment methods
  • Dashboard methods

inside LoginPage.


6. Combine Fixtures with POM

Enterprise projects usually combine:

Fixtures

Page Objects

Tests

This produces a clean and reusable framework.


7. Reuse Components

Menus, headers, and footers should become reusable component classes instead of being duplicated across pages.


Common Mistakes to Avoid

Mistake 1: Writing Locators Inside Tests

Avoid:

await page.locator(‘#username’)

Instead:

loginPage.login()


Mistake 2: Huge Page Classes

Don’t create a 1000-line Page Object.

Split functionality into:

  • Components
  • Multiple pages
  • Helper classes

Mistake 3: Hardcoding Test Data

Instead of:

login(“admin”,”123″)

Read data from:

data/users.json


Mistake 4: Mixing Assertions with Page Logic

Bad:

loginPage.login();

expect(page).toHaveURL(…);

inside LoginPage.

Assertions belong in test files.


Mistake 5: Duplicate Components

If multiple pages contain the same navigation menu, create a shared component.


Selenium to Playwright POM Migration Tips

If you’re moving from Selenium, you’ll notice that the Page Object Model concept remains the same, but Playwright simplifies many tasks.

Selenium POMPlaywright POM
WebDriverBuilt-in Page object
PageFactory (optional)Modern locator API
Explicit waits frequently requiredBuilt-in Auto Waiting
Separate wait utilitiesAutomatic synchronization
More boilerplateCleaner code

Migration tips:


Playwright Page Object Model Interview Questions

1. What is the Playwright Page Object Model?

Answer:
It is a design pattern that separates page interactions from test logic using reusable page classes.


2. Why should we use POM?

Answer:

  • Code reuse
  • Better maintenance
  • Cleaner tests
  • Improved scalability

3. What is stored inside a Page Object?

Answer:

  • Locators
  • Page methods
  • Navigation methods
  • Business actions

4. What should not be stored in a Page Object?

Answer:

Assertions and test validations should remain in test files.


5. What is the constructor used for?

Answer:

It receives the Playwright Page object so that all methods can interact with the browser.


6. How do you create reusable Page Objects?

Answer:

Create one class per page and expose reusable methods such as login(), searchProduct(), or logout().


7. What are reusable components?

Answer:

Shared UI elements such as headers, sidebars, menus, and footers that can be represented as separate classes.


8. Can POM be used with Fixtures?

Answer:

Yes. Fixtures prepare reusable resources, while Page Objects encapsulate page interactions.


9. Why is POM important in enterprise automation?

Answer:

It improves maintainability, supports team collaboration, and scales well for large applications.


10. How does Playwright improve POM compared to Selenium?

Answer:

Playwright provides Auto Waiting, modern locator APIs, and built-in test runner features that reduce boilerplate code.


Frequently Asked Questions (FAQs)

What is the Playwright Page Object Model?

The Playwright Page Object Model is a design pattern that organizes page interactions into reusable classes, separating them from test logic.


Is Playwright POM suitable for beginners?

Yes. Beginners can start with a single page class and gradually build reusable components as projects grow.


Can I use Playwright POM with TypeScript?

Yes. TypeScript is one of the most popular languages for implementing Playwright Page Object Model frameworks.


What are the advantages of using POM?

POM improves code reuse, readability, maintainability, scalability, and simplifies updates when the UI changes.


Should assertions be inside Page Objects?

Generally, no. Keep assertions in test files while Page Objects focus on page interactions.


Can POM be combined with Fixtures?

Yes. Combining Fixtures, Browser Contexts, and Page Objects is a common enterprise framework pattern.

Leave a Comment

Your email address will not be published. Required fields are marked *