Before you write a single page object, decide where files go. A messy folder structure kills the benefits of POM. You will waste more time finding page objects than the time you save by using them. Let me show you the structure that works for real projects.
project-root/
├── playwright.config.ts
├── pages/ # All page objects live here
│ ├── base.page.ts # BasePage -- common methods
│ ├── login.page.ts # LoginPage
│ ├── dashboard.page.ts # DashboardPage
│ ├── transfer.page.ts # TransferPage
│ └── components/ # Reusable UI components
│ ├── header.component.ts
│ ├── sidebar.component.ts
│ └── modal.component.ts
├── fixtures/ # Custom test fixtures
│ └── pages.fixture.ts # Extend test with page objects
├── tests/ # Test files
│ ├── login.spec.ts
│ ├── dashboard.spec.ts
│ └── transfer.spec.ts
└── test-data/ # Test data files
└── users.ts| File Type | Naming Pattern | Example |
|---|---|---|
| Page object | <name>.page.ts | login.page.ts, dashboard.page.ts |
| Component | <name>.component.ts | header.component.ts, modal.component.ts |
| Fixture | <name>.fixture.ts | pages.fixture.ts, auth.fixture.ts |
| Test file | <name>.spec.ts | login.spec.ts, transfer.spec.ts |
| Test data | <name>.ts or <name>.json | users.ts, accounts.json |
| Class name | PascalCase + suffix | LoginPage, HeaderComponent, DashboardPage |
Always suffix page class names with Page and component classes with Component. LoginPage, not Login. HeaderComponent, not Header. When you have 20+ classes, the suffix tells you what you are looking at without opening the file.
Create pages/, fixtures/, tests/, test-data/ folders at project root
Create base.page.ts with common navigation and utility methods
Create one page object per page in your application
Extract repeated UI sections into components/ folder
Create a fixture file that extends test with your page objects
Write tests using fixtures -- never instantiate page objects manually in tests
Do not create a pages/ folder inside tests/. Keep them separate. Page objects are not tests. They are reusable building blocks. When a new team member joins, they should see pages/ and immediately know: this is where all UI interactions are defined.
Q: How do you organize page objects in a Playwright project?
A: I use a flat structure: pages/ for page objects, components/ for reusable UI components, fixtures/ for custom test fixtures, and tests/ for spec files. Each page object follows <name>.page.ts naming. Classes are PascalCase with a Page suffix -- LoginPage, DashboardPage. I also create a BasePage with shared methods like goto() and waitForLoad(). Components handle repeating UI sections like headers and modals. Fixtures wire everything together so tests get page objects as parameters instead of creating them manually.
Key Point: Structure matters. pages/ for page objects, components/ for reusable UI sections, fixtures/ for wiring. Name files as <name>.page.ts, classes as PascalCase + Page suffix.