Page Objects and Component Objects at Framework Scale
Extending the page-object idea from a single page to reusable component objects for UI pieces that appear across many pages, and deciding when a full page object is actually justified.
What you'll learn
- Explain why a component object (for a UI piece reused across many pages) is a distinct, useful idea from a page object
- Design a component object's responsibilities so it stays reusable across the pages that embed it
- Decide when a full page object is justified vs. when it's unnecessary overhead for a framework
Prerequisites
Explanation
No real page or component executes in this lesson's exercises -- they model page-object and component-object responsibility decisions as data, using genuine JavaScript/TypeScript execution.
A page object wraps one specific page's locators and actions behind a small, meaningful API — this idea should already be familiar from a foundational Playwright course. At framework scale, a genuinely common situation arises: some UI pieces (a navigation bar, a course-card component, a confirmation modal) appear on many different pages, not just one. Encoding that shared UI's locators and interactions separately inside every page object that happens to embed it duplicates the exact same logic across all of them, and a change to that shared piece then requires updating every duplicated copy — precisely the same maintenance problem this course has already covered for setup logic and test data.
A component object solves this the same way a fixture solves duplicated setup: it's a small, focused class or object representing one reusable UI piece, owning that piece's locators and interactions in exactly one place — and any page object whose page happens to embed that component simply holds an instance of it, rather than re-declaring its locators. A NavigationBar component object, for example, can be instantiated by a DashboardPage, a CourseOverviewPage, and a ProfilePage alike, each just delegating navigation-related actions to the same shared component instance — a change to the nav bar's markup or behavior is then a one-place fix, not a scattered one.
Not every piece of UI justifies a dedicated page or component object — this is a genuine, honest tradeoff worth naming explicitly: a page or component visited or interacted with by only a single test, with simple, one-off locators, may not be worth the abstraction overhead of a dedicated class at all — a plain, inline locator inside that one test can be perfectly appropriate. The decision mirrors the fixture-depth tradeoff from the previous lesson: extract a page or component object when it's genuinely reused or meaningfully complex, not mechanically for every page or UI element a framework happens to touch.
Example
Modeling detecting shared-component duplication across page objects, and a page-object-worthiness decision, as data.
function findDuplicatedComponentUsage(pageObjectDefinitions) {
// Models detecting the SAME component's locators declared inside multiple page objects independently.
const componentLocatorCounts = {};
for (const page of pageObjectDefinitions) {
for (const component of page.inlineComponents) {
componentLocatorCounts[component] = (componentLocatorCounts[component] ?? 0) + 1;
}
}
return Object.entries(componentLocatorCounts).filter(([, count]) => count > 1).map(([name]) => name);
}
const pages = [
{ name: "DashboardPage", inlineComponents: ["navBar"] },
{ name: "ProfilePage", inlineComponents: ["navBar"] },
{ name: "LoginPage", inlineComponents: [] },
];
console.log(findDuplicatedComponentUsage(pages)); // ["navBar"] -- duplicated across 2 page objects, a real extraction candidate
function isPageObjectJustified(usedByMultipleTests, hasNonTrivialInteractions) {
return usedByMultipleTests || hasNonTrivialInteractions;
}
console.log(isPageObjectJustified(false, false)); // false -- a single, simple, one-off locator doesn't need a dedicated classTry it yourself
Call findDuplicatedComponentUsage against a list where NO component repeats across page objects, and confirm it correctly returns an empty array.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
This models finding which components are genuinely worth extracting only -- no real page object is scanned. Write componentsWorthExtracting(pageObjectDefinitions), returning an array of component names used by MORE than one page object, sorted alphabetically.
Checks: correctly identifies multiple genuinely shared components, sorted · correctly excludes a component used by only one page · correctly handles an empty input
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
This models the page-object-worthiness decision only -- no real page is analyzed. Write isPageObjectJustified(usedByMultipleTests, interactionCount): return true if usedByMultipleTests is true, OR interactionCount is greater than 3 (a simple threshold modeling non-trivial complexity).
Checks: justifies extraction based on reuse alone · justifies extraction based on complexity alone, even without reuse · correctly avoids unnecessary extraction for simple, single-use interactions
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Common mistakes
- Duplicating a shared UI piece's locators (like a navigation bar) separately inside every page object that embeds it, instead of extracting one reusable component object -- a later markup change then requires updating every duplicated copy.
- Mechanically creating a dedicated page object class for every single page a framework touches, even ones visited by exactly one simple test -- this adds real overhead without a corresponding real benefit.
- Giving a component object responsibilities beyond the UI piece it represents (like unrelated page-level navigation logic) -- this couples it to a specific page's context and undermines its reusability across the pages that embed it.
Knowledge check
Takeaway
Extract a component object for UI pieces genuinely reused across multiple pages, to avoid duplicating their locators and interactions. Reserve a dedicated page object for pages that are either reused across multiple tests or meaningfully complex -- a simple, single-use page doesn't require the abstraction.
Summary
A component object centralizes a shared UI piece's (like a nav bar's) locators and interactions in one reusable place, avoiding duplication across every page object that embeds it -- page objects hold and delegate to component instances rather than re-declaring their locators. Both page and component objects are worth their overhead specifically when there's genuine reuse or meaningful complexity, not mechanically for every page a framework touches.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.