FRAMEWORK DESIGNINTERMEDIATE

Playwright Base Page Design: Build a Reusable Foundation for Your Automation Framework

Learn how to design a Playwright Base Page class that eliminates duplication across Page Objects with reusable navigation, waiting, screenshot, scroll, and dialog methods.

iff Solution Academy July 28, 2026 14 min read Updated July 28, 2026
Playwright Base Page BasePage Page Object Model Framework Design Reusable Methods TypeScript Enterprise Framework Best Practices

Introduction

Once you've implemented the Page Object Model (POM), you'll quickly notice another problem.

Many of your page classes contain the same code: opening URLs, waiting for elements, clicking buttons, typing into text boxes, taking screenshots, waiting for loaders to disappear, reading toast messages, scrolling to elements, and handling dialogs.

If every Page Object repeats these methods, your framework becomes difficult to maintain. Professional automation frameworks solve this by introducing a Base Page. A Base Page is a parent class that contains all the common functionality shared across multiple pages. Every Page Object extends this class and automatically inherits those reusable methods.

In this tutorial, you'll learn why a Base Page is important, how to design one in Playwright, what methods belong in it, what should never go inside it, and how enterprise teams structure their automation frameworks.

What is a Base Page?

A Base Page is a reusable parent class that contains common functionality used by multiple pages. Instead of writing the same methods repeatedly, every Page Object inherits them. Think of it as the foundation of your automation framework.

text
                BasePage
                    │
      ┌─────────────┼─────────────┐
      │             │             │
 LoginPage    DashboardPage   ProductsPage
      │             │             │
      └─────────────┼─────────────┘
           Shared Common Methods

This keeps your framework clean, consistent, and easier to maintain.

Why Do You Need a Base Page?

Imagine you have 80 Page Objects, 2,500 automated tests, and 12 automation engineers. Every page needs methods like navigate, wait, screenshot, scroll, and verify page title. Without a Base Page, these methods would be duplicated dozens of times. With a Base Page, they're written once and reused everywhere.

Project Structure

A clean enterprise framework might look like this:

text
playwright-framework/

pages/
│
├── BasePage.ts
├── LoginPage.ts
├── DashboardPage.ts
├── ProductsPage.ts
├── CheckoutPage.ts

utils/
fixtures/
tests/
api/
data/

Every page inherits from BasePage.

Creating the Base Page

Create pages/BasePage.ts:

pages/BasePage.ts
import { Page, Locator } from '@playwright/test';

export class BasePage {

    constructor(protected page: Page) {}

}

Every child page will now have access to the Playwright page object.

Instead of writing await page.goto('/login'); everywhere, create a reusable navigate method.

typescript
async navigate(url: string) {

    await this.page.goto(url);

}

Usage:

typescript
await loginPage.navigate('/login');

Simple and reusable.

Get Current URL

typescript
async currentUrl() {

    return this.page.url();

}

Usage:

typescript
const url = await loginPage.currentUrl();

Get Page Title

typescript
async title() {

    return await this.page.title();

}

Useful for validations and debugging.

Refresh Page

typescript
async refresh() {

    await this.page.reload();

}

Go Back

typescript
async back() {

    await this.page.goBack();

}

Go Forward

typescript
async forward() {

    await this.page.goForward();

}

Take Screenshot

Screenshots are extremely useful when debugging failed tests.

typescript
async screenshot(name: string) {

    await this.page.screenshot({

        path: `screenshots/${name}.png`

    });

}

Usage:

typescript
await dashboardPage.screenshot('Dashboard');

Wait for Page Load

Instead of repeating await page.waitForLoadState('networkidle'); create a reusable method.

typescript
async waitForPageLoad() {

    await this.page.waitForLoadState('networkidle');

}

Scroll to Element

typescript
async scroll(locator: Locator) {

    await locator.scrollIntoViewIfNeeded();

}

Many applications require scrolling before interacting with elements.

JavaScript Click

Occasionally, standard clicks don't work due to overlays or custom UI components.

typescript
async jsClick(locator: Locator) {

    await locator.evaluate((element) => {

        (element as HTMLElement).click();

    });

}

Use this sparingly and only when necessary.

Highlight an Element

Helpful for debugging.

typescript
async highlight(locator: Locator) {

    await locator.evaluate((element) => {

        (element as HTMLElement).style.border =
            '3px solid red';

    });

}

Many automation engineers enable this during local debugging.

Wait for Loader

Enterprise applications often display loading spinners.

typescript
async waitForLoader(loader: Locator) {

    await loader.waitFor({

        state: 'hidden'

    });

}

Now every page can use the same waiting strategy.

Read Toast Messages

Many applications display success notifications.

typescript
async getToastMessage(toast: Locator) {

    return await toast.textContent();

}

Reusable across every page.

Handling Dialogs

typescript
async acceptDialog() {

    this.page.on('dialog', async dialog => {

        await dialog.accept();

    });

}

Similarly:

typescript
async dismissDialog() {

    this.page.on('dialog', async dialog => {

        await dialog.dismiss();

    });

}

Waiting for URL

typescript
async waitForUrl(url: string) {

    await this.page.waitForURL(url);

}

Useful after navigation.

Common Keyboard Actions

typescript
async pressEnter() {

    await this.page.keyboard.press('Enter');

}

Download Handling

Many enterprise applications export reports.

typescript
const downloadPromise = this.page.waitForEvent('download');

Wrapping download logic in the Base Page avoids repeating the same implementation.

Extending BasePage

Now your Login Page becomes:

pages/LoginPage.ts
import { BasePage } from './BasePage';

export class LoginPage extends BasePage {

    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();

    }

}

Notice that LoginPage automatically inherits every Base Page method.

Combining Base Page with Locator Helpers

If you've created custom locator wrapper functions, your page becomes even cleaner. Instead of this.page.getByRole('button', { name: 'Login' }); you can simply write this.locator.button('Login');. This keeps locator strategies centralized and consistent across the framework.

pages/LoginPage.ts
import { BasePage } from './BasePage';
import { LocatorHelper } from '../utils/LocatorHelper';

export class LoginPage extends BasePage {
  private locator = new LocatorHelper(this.page);

  async login(username: string, password: string) {
    await this.locator.textbox('Username').fill(username);
    await this.locator.textbox('Password').fill(password);
    await this.locator.button('Login').click();
  }
}

What Should NOT Go Into Base Page?

A common mistake is turning the Base Page into a God Class. Avoid placing page-specific business logic here. For example, login(), checkout(), addProduct(), and searchProduct() do not belong in the Base Page. These methods belong in their respective Page Objects. The Base Page should only contain generic functionality shared across many pages.

Best Practices

  • Keep the Base Page focused on reusable utilities.
  • Use inheritance only for truly common behavior.
  • Prefer Playwright's built-in APIs before creating custom wrappers.
  • Keep methods small and easy to understand.
  • Combine the Base Page with the Page Object Model and Fixtures.
  • Document shared methods so new team members can discover them easily.

Common Mistakes

  • Making the Base Page hundreds of lines long.
  • Adding business workflows to the Base Page.
  • Duplicating helper methods in child pages.
  • Ignoring Playwright's built-in waiting mechanisms.
  • Mixing API utilities into UI Base Pages.

Enterprise Framework Example

A mature Playwright framework often looks like this:

text
pages/

BasePage.ts

LoginPage.ts

DashboardPage.ts

ProductsPage.ts

OrdersPage.ts

CheckoutPage.ts

AdminPage.ts

ProfilePage.ts

Every page inherits navigation, waiting, screenshots, scrolling, and shared UI utilities from BasePage, while each child class contains only the functionality unique to that page.

Frequently Asked Questions

Is a Base Page mandatory?

No. Small projects can work without one. However, for medium and large automation frameworks, a Base Page greatly improves consistency and reduces code duplication.

Should every Page Object extend BasePage?

Usually yes, provided the Base Page contains only generic UI functionality. If a page has unique behavior that doesn't benefit from inheritance, composition is another valid design choice.

Can I have multiple Base Pages?

Absolutely. Many enterprise frameworks create specialized base classes such as BasePage, AuthenticatedPage, AdminBasePage, and ModalBasePage. Each serves a different purpose while keeping inheritance focused.

Should assertions go in the Base Page?

No. The Base Page should provide reusable utilities. Assertions generally belong in your test files or dedicated assertion/helper classes.

Conclusion

A well-designed Base Page is one of the cornerstones of a scalable Playwright automation framework. It eliminates duplication, promotes consistency, and allows Page Objects to focus on business actions instead of low-level browser interactions.

The key is balance: keep the Base Page lightweight, reusable, and framework-oriented. Combine it with the Page Object Model, custom locator helpers, fixtures, and environment configuration, and you'll have a solid foundation that can support thousands of automated tests.

In the next tutorial, we'll explore the Playwright Component Object Model (COM), where you'll learn how to model reusable UI components like headers, navigation menus, sidebars, modals, and data tables.

  • Playwright Page Object Model (POM)
  • Custom Locator Helper Functions
  • Playwright Project Structure Explained
  • Playwright Fixtures Explained
  • Playwright Component Object Model
  • Enterprise Playwright Framework Architecture
  • Playwright Best Practices
  • Playwright Design Patterns
  • Playwright Interview Questions
  • Playwright Utility Classes

Keywords: Playwright Base Page, Playwright BasePage, Base Page Design, Playwright framework architecture, Playwright Page Object Model, Playwright reusable methods, Playwright automation framework, Playwright TypeScript, enterprise Playwright framework, Playwright best practices.

Get Playwright tutorials in your inbox

Weekly tips, real-world examples, and framework patterns – no spam, unsubscribe anytime.

Playwright Framework Series

View all →
  1. 1Getting Started with Playwright: Installation, Setup, and Your First Test
  2. 2Playwright Locators: The Complete Guide with Real-World Examples
  3. 3Playwright Actions: Complete Guide to Click, Fill, Hover, Keyboard, Mouse & File Upload
  4. 4Playwright Assertions: The Complete Guide with Real-World Examples
  5. 5Playwright Auto Waiting: The Complete Guide with Real-World Examples
  6. 6Playwright Fixtures: The Complete Guide with Real-World Examples
  7. 7Playwright Browser Context & Multiple Tabs: The Complete Guide with Real-World Examples
  8. 8Playwright Authentication & Session Management: Complete Guide with Enterprise Examples
  9. 9Playwright Network Interception & API Mocking: Complete Guide with Real-World Examples
  10. 10Playwright Page Object Model (POM): The Complete Guide with Enterprise Examples
  11. 11Page Object Model with Playwright: A Practical Guide
  12. 12How to Build a Robust Professional Playwright Framework from Scratch (Step-by-Step)
  13. 13Building an Enterprise Playwright Framework from Scratch

API Testing Series

View all →
  1. 1Playwright API Testing: The Complete Guide with Real-World Examples
  2. 2Part 1 : Playwright API Testing Tutorial: Build Enterprise-Level API Automation Framework
  3. 3Part 2: Playwright API Testing Tutorial: Setting Up Project & Sending Your First API Request
  4. 4Part 3: Mastering CRUD Operations in Playwright API Testing
  5. 5Part 4: Authentication in Playwright API Testing (Bearer Token, JWT, API Key & Reusable Fixtures)
  6. 6Part 5: Building an Enterprise-Level Playwright API Automation Framework
  7. 7Part 6: API Models, Schema Validation & Test Data Management in Playwright
  8. 8Part 7: Custom Fixtures, Hooks, Logging & Parallel Execution in Playwright API Testing
  9. 9Part 8: Building a Production-Ready Playwright API Framework (Environment Management, CI/CD, Reporting & Best Practices)
  10. 10Part 9: Advanced Playwright API Testing Techniques for Enterprise Automation
  11. 11Part 10: Building a Complete Enterprise Playwright API Automation Framework (Final Part)
  12. 12API Testing with Playwright: Request Context, Auth, and Assertions

Recommended Next Articles