← All Posts

The Complete Guide to Playwright Page Object Model

2/10/2025

When your Playwright test suite grows past 50 tests, you'll start noticing duplication. The same selectors appear in multiple files. A UI change forces you to update 15 tests. That's where Page Object Model comes in.

What is Page Object Model?

POM is a design pattern where you create a class for each page (or component) of your application. The class encapsulates the selectors and actions for that page. Tests interact with the page object, not with raw selectors.

Basic example

// pages/login.page.ts
import { Page } from '@playwright/test';

export class LoginPage {
  constructor(private page: Page) {}

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.page.fill('[data-testid="email"]', email);
    await this.page.fill('[data-testid="password"]', password);
    await this.page.click('[data-testid="submit"]');
  }

  async expectError(message: string) {
    await expect(
      this.page.getByText(message)
    ).toBeVisible();
  }
}

Using fixtures for DI

Instead of instantiating page objects in every test, use Playwright's fixture system to inject them automatically:

// fixtures.ts
import { test as base } from '@playwright/test';
import { LoginPage } from './pages/login.page';

export const test = base.extend<{
  loginPage: LoginPage;
}>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
});

When NOT to use POM

Don't create page objects for everything. If a page is only tested in one place, inline the selectors. POM adds indirection, so only add it when you have genuine duplication across 3+ tests.

Production patterns

  • Composition over inheritance: Don't create deep page object hierarchies. Prefer small, composable page objects.
  • Data-testid selectors: Use stable selectors like data-testid instead of CSS classes or text content.
  • Assertions in page objects: It's fine to have assertion methods on page objects (like expectError above). They reduce test verbosity.
  • Type safety: Use TypeScript to get autocomplete and catch typos at compile time.

Need help structuring your Playwright test architecture? Get in touch. We set up test infrastructure for teams across SaaS and e-commerce.

Need Help Implementing This?

We help engineering teams set up test automation, CI/CD, and quality infrastructure.