Playwright Page Object Model (POM) Best Practices with Examples
Playwright has rapidly become the go-to automation testing framework for modern web applications. When combined with a well-designed Page Object Model (POM), Playwright delivers fast, reliable, and highly maintainable test suites that scale effortlessly. In this guide, you will learn Playwright POM best practices, complete with real code examples and advanced features like parallel execution, cross-browser testing, retries, tagging, and HTML reports.
This article is written for SDETs, QA engineers, and test automation leads who want production-grade Playwright automation.
What Is Page Object Model (POM) in Playwright?
The Page Object Model is a design pattern where each application page (or component) is represented as a class. This class encapsulates:
Locators
Page actions
Page-specific assertions
Instead of scattering selectors across tests, POM centralizes UI knowledge, making tests easier to read and maintain.
Why POM + Playwright works so well:
Playwright locators are resilient and auto-wait by default
Native async handling avoids flaky timing issues
Built-in test runner supports advanced execution patterns
Clean separation between test logic and UI details improves maintainability and reduces duplication.
Recommended Playwright POM Folder Structure
playwright-project/ ├── pages/ │ ├── login.page.ts │ ├── dashboard.page.ts │ └── base.page.ts ├── tests/ │ ├── login.spec.ts │ └── dashboard.spec.ts ├── playwright.config.ts └── package.json
This structure keeps pages reusable and tests clean. Consider adding a components/ folder for reusable UI elements like headers or modals as your project grows.
Creating a Base Page (Best Practice)
A base page helps avoid duplication across page objects.
import { Page } from '@playwright/test'; export class BasePage { protected page: Page; constructor(page: Page) { this.page = page; } async navigate(url: string) { await this.page.goto(url); } }
Extend this with common utilities like wait methods, screenshot capture, or navigation helpers to maximize reuse.
Example: Login Page Object
import { Locator, Page, expect } from '@playwright/test'; import { BasePage } from './base.page'; export class LoginPage extends BasePage { readonly username: Locator; readonly password: Locator; readonly loginButton: Locator; constructor(page: Page) { super(page); this.username = page.getByLabel('Username'); this.password = page.getByLabel('Password'); this.loginButton = page.getByRole('button', { name: 'Login' }); } async login(user: string, pass: string) { await this.username.fill(user); await this.password.fill(pass); await this.loginButton.click(); } async assertLoginSuccess() { await expect(this.page).toHaveURL(/dashboard/); } }
Notice how all login-related logic is encapsulated in one class, making it easy to update when the UI changes.
Writing a Test Using POM
import { test } from '@playwright/test'; import { LoginPage } from '../pages/login.page'; test('User can login successfully', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.navigate('https://example.com/login'); await loginPage.login('admin', 'password123'); await loginPage.assertLoginSuccess(); });
Tests remain short, readable, and business-focused. The test reads like a user story, hiding complex selector logic.
Parallel Execution in Playwright POM
Playwright runs tests in parallel by default using workers.
// playwright.config.ts import { defineConfig } from '@playwright/test'; export default defineConfig({ workers: 4 });
POM advantage:
Each test gets a fresh browser context
Page objects remain stateless and thread-safe
Stateless POM classes ensure parallel tests don't interfere with each other, enabling faster test execution.
Cross-Browser Testing with POM
Playwright supports Chromium, Firefox, and WebKit out of the box.
projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'Firefox', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ]
Your same POM classes work across all browsers without changes. Write once, run everywhere—Playwright handles browser-specific quirks internally.
Built-In Retries for Stable Test Suites
Retries help handle intermittent infra or network issues.
retries: process.env.CI ? 2 : 0
Playwright retries only failed tests, not entire suites. This reduces flakiness in CI/CD pipelines where network issues are more common.
Test Tagging with Playwright POM
Tagging helps organize large test suites.
test('@smoke @login User login test', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.login('admin', 'password'); });
Run tagged tests:
npx playwright test --grep @smoke
Tagging enables selective test execution—run smoke tests before deployments or regression tests nightly.
HTML Reports in Playwright
Playwright generates rich HTML reports automatically.
reporter: [['html', { open: 'never' }]]
View reports:
npx playwright show-report
Reports include:
Step logs
Screenshots
Videos
Trace viewer
These reports are invaluable for debugging failed tests, showing exactly what happened during test execution.
POM Best Practices Checklist
Use
getByRoleandgetByTestIdlocators for accessibility and stabilityAvoid assertions inside page objects (except page-level checks)
Keep page objects action-focused
Do not store test data inside POMs
Prefer composition over inheritance when pages grow
Following these practices ensures your POM stays maintainable as your application evolves.
When NOT to Use POM
Very small prototypes
One-off UI checks
Heavy API-only test suites
In such cases, simple test scripts may be sufficient. Don't over-engineer—use POM when you need long-term maintainability.
👉 Suggested Article: Playwright Automation Testing: Complete Guide (2026)
Conclusion
Playwright Page Object Model best practices enable teams to build scalable, readable, and stable automation frameworks. When combined with Playwright's parallel execution, cross-browser support, retries, tagging, and HTML reports, POM becomes a powerful foundation for enterprise-grade test automation.
If you are building long-term UI automation, Playwright POM is a future-proof choice. The combination of Playwright's modern architecture with POM's organizational structure creates a robust testing solution that scales with your application's complexity.
📘Tutorials | 🧠AI | 🧪Selenium | 🥇Top 10 | 🛠️Tools | 📋Software Testing