Skip to content

How To Build a Playwright Page Object Model

In my previous posts, I have already covered Microsoft Playwright installation, its features and architecture and how it is different from other automation tools. Furthermore, I also demonstrated how to write the first test script using the Playwright record and playback tool Codegen. In this article, I will cover how to build a Playwright Page Object Model.

Before proceeding, make sure you’ve read the following articles related to the Playwright framework:

  1. Playwright Vs Cypress Vs Selenium
  2. Playwright Installation Guide
  3. How to record and generate a script using Codegen

What is the Page Object Model(POM)

The Page Object Model is a design pattern where every page of the application is represented by a unique “Page Object”.A Page Object represents a page in the application under test.

Page Objects contain locators or identifiers required to interact with elements(button, textbox, dropdown etc) and methods to perform the actions on the elements. The test scripts use Page Objects to perform actions and make assertions about the application’s behaviour.

Implementing Playwright Page Object Model

In this article, I’ll be utilizing the same scenario/script recorded using the Playwright Codegen tool in the previous article.

App Url: OrangeHRM demo site

Demo app URL :


  1. Visual Studio Code Editor
  2. Node Js Installation
  3. Playwright Installation and Setup.

Project Hierarchy

  1. Create a Project folder and open it inside the VSCode editor.
  2. Install Playwright and the required browsers using the below commands.
npm init playwright@latest

3. Create a new folder named “page-objects” to store all the relevant locators and actions.
4. Create another folder “utility” to contain utility functions like reading JSON data.

Playwright Page Object Model

Creating a Page Class in Playwright:

Let’s start by considering a common scenario – the login page. To create a Page Class for the login page in Playwright, you’ll first define the necessary element locators and methods within the class.

1. Go to the page-objects folder and create a new page-object class, naming it as “loginPage.js”.

2. In the login page class add the locators to identify the elements of the login page and add methods to perform actions on those web elements. Refer below code for the login page Object.

class loginPage{
    { =page;
        this.Username = page.getByPlaceholder('Username');
        this.Password = page.getByPlaceholder('Password');
        this.Loginbutton = page.getByRole('button', { name: 'Login' });
        this.DashboardHeading = page.getByRole('heading', { name: 'Dashboard' });


    async goto()

    async validUserLogin(userName,password)
      await this.Username.type(userName);
      await this.Password.type(password);

module.exports ={loginPage};

In this example, the loginPage class encapsulates the element locators and the validUserLogin method, abstracting away the complexities of the login page. The page object is passed to the constructor, providing access to Playwright’s functionalities.

Writing the Test Script

The next step is to implement the test class. All the test scripts should be present inside the test folder.

1. Go to the tests folder and create a new test file inside it, naming it “logintest.spec.js”. Import the previously created page object class “loginPage.js” from the page-objects folder directly into this test class.

const { test, expect } = require('@playwright/test');
const {loginPage} = require('../page-objects/loginPage');

2. Use fixtures and assertions to write the test functions and perform assertions. Refer below code for complete reference.

const { test, expect } = require('@playwright/test');
const {loginPage} = require('../page-objects/loginPage');

test.describe('Test Suite', () => {

test('Client App login', async ({page})=>
  const username = "admin"
  const password = "admin123"
  const loginpage = new loginPage(page);
    await page.goto('/')
    await loginpage.validUserLogin(username,password);
    await expect(loginpage.DashboardHeading).toBeVisible()

In the above test script, we have created a test with a page fixture. An object is created for the login page class. By using this object, actions have been performed. With the help of the “Playwright except” library, we have added an assertion to validate that the user successfully logged in and navigated to the dashboard screen.

To test the script execute the below-mentioned command:

npx playwright test logintest.spec.js

This command will execute the specified test file and display the test result on the console.

Reading Test Data from JSON in Playwright

To achieve the reusability and maintainability of the framework it is important to keep the test data and logic separate. Having a central data repository ensures that the test data remains consistent across different test scripts and test runs. This helps in maintaining the accuracy and reliability of your tests.

Creating a Test Data file

In the previous example, we used the test data(username & password) in our test script directly, let’s update it. It will help us in scenarios where test data changes or evolves, so we can update the data files without modifying the test scripts, thus reducing the efforts required to maintain the tests.

Go to the project directory and create a new folder “utils”. Inside this folder create a new file “testDataUtils.json”. Inside this file add the JSON data which is required for our test script.

     "username" : "Admin",
     "password" : "admin123",
     "fullname" : "Mario",
     "lastname" : "James",

To read this JSON file we need to import this in our test script. Go to “loginPage.js” and add the below line.

const dataset = JSON.parse(JSON.stringify(require('../utils/testDataUtils.json')))

This line reads the content of ‘testDataUtils.json’, and converts it into a JSON-formatted string using the JSON.stringify() method, and then subsequently parses it back into a JavaScript object using JSON.parse() method. The resulting data is then stored in the dataset.

Remove the test data from the test script as we have already stored it in a JSON file and imported that JSON file into our test script. In the validUserLogin method pass the username and password from the dataset.

const { test, expect } = require('@playwright/test');
const {loginPage} = require('../page-objects/loginPage');
const dataset = JSON.parse(JSON.stringify(require('../utils/testDataUtils.json')));

test.describe('Test Suite', () => {

test('Client App login', async ({page})=>
  await page.goto('/')
  await loginpage.validUserLogin(dataset.username,dataset.password);
  await expect(loginpage.DashboardHeading).toBeVisible()

Execute the script and validate the result. By following this approach we can also maintain multiple test data sets for different environments.


In this post, we’ve explored the practical implementation of the Page Object Model in the Playwright test automation framework. We have also separated the test data from the test logic and established a structured and modular approach to enhance the readability and maintainability of our test scripts for the framework. Feel free to share your thoughts in the comment section.