Page Object
Page Objects encapsulate locators and user actions for a page or component. They reduce duplication and keep specs focused on behavior, not selector details.
Why Use Page Objects? When the same selectors and action sequences appear in many tests, a Page Object class centralizes them. Update the login button locator once, not in twenty specs.
Playwright's locators work naturally as class fields — no special base class required. Constructor receives Page and exposes methods like login(email, password).
import { type Page, type Locator } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly email: Locator;
readonly password: Locator;
readonly submit: Locator;
constructor(page: Page) {
this.page = page;
this.email = page.getByLabel('Email');
this.password = page.getByLabel('Password');
this.submit = page.getByRole('button', { name: 'Sign in' });
}
async goto() { await this.page.goto('/login'); }
async login(email: string, password: string) {
await this.email.fill(email);
await this.password.fill(password);
await this.submit.click();
}
} Locators are defined once in the constructor and reused by methods.
Using a Page Object in a Test import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/login-page';
test('dashboard after login', async ({ page }) => {
const login = new LoginPage(page);
await login.goto();
await login.login('user@example.com', 'secret');
await expect(page).toHaveURL(/dashboard/);
}); Instantiate with the test's page fixture. Methods return promises; always await them in tests. Keep assertions in tests, not buried inside page objects (usually). One class per page or major UI region is a common split. Page Object Quick Reference Patterns that work well with Playwright.
Practice Recommendation Locator storage Fields on the class, set in constructor Navigation goto() method on the page object Assertions Keep in test unless checking page-internal state Composition Nested components for shared widgets Fixtures Optional: register page objects as custom fixtures Naming LoginPage, CheckoutPage — match routes
Page Objects as Fixtures (Preview) For larger suites, register page objects as fixtures so tests receive loginPage directly. The Page Object Model lesson expands this pattern.
// playwright/support/pages.fixture.ts pattern
test.extend<{ loginPage: LoginPage }>({
loginPage: async ({ page }, use) => {
await use(new LoginPage(page));
},
}); Injecting Page Objects via Fixtures Combine page objects with test.extend() so specs receive ready-made LoginPage instances without manual construction in every test.
Common Mistakes Page objects that assert business outcomes — blurs test vs page responsibility. God objects with every locator in the app. Storing Page references across tests instead of per-test instances. Wrapping every single click in a one-line method with no added value. Key Takeaways Page Objects centralize locators and actions per page. Playwright locators fit cleanly as class fields. Keep tests readable; page objects hide selector churn. Fixtures can inject page objects for larger suites.
Pro Tip
Start without page objects; extract one when you copy-paste the same three locators twice. Premature abstraction slows early learning.
Browser Context Go to next item