
Learn how to handle frames and windows in Playwright, including interacting with frames, multiple tabs, pop-ups, and nested pages efficiently.
Last Modified on: December 1, 2025
Frames and windows are commonly used to display third-party content, embedded pages, or external resources within a website. In automation testing, handling frames and windows in Playwright can be challenging because tests must correctly switch between different browsing contexts, whether it's an iframe, a new tab, or a separate browser window.
Playwright streamlines this process through clean, intuitive APIs that make switching between frames, interacting with pop-ups, and managing multiple tabs both reliable and efficient. Understanding how to handle frames and windows in Playwright is essential for building stable and effective end-to-end test automation.
Overview
How to Handle Frames in Playwright?
Handling frames in Playwright involves accessing elements inside <iframe>containers instead of the main page context. Since frames load external or embedded content, Playwright provides dedicated APIs to locate, switch, and interact with them safely. Understanding how to handle frames in Playwright ensures your tests can work with nested UI components, embedded forms, and multi-frame layouts.
Steps to Handle Frames in Playwright:
How to Handle Windows in Playwright?
Handling windows in Playwright is simple because each new tab or pop-up is treated as a separate Page object. You can wait for the new windows using the page.waitForEvent("popup"), interact with them just like any other page, and switch between multiple tabs seamlessly. This makes it easy to test user flows that involve pop-ups or external links.
Steps to Handle Windows in Playwright:
What Are Advanced Techniques for Handling Windows in Playwright?
Advanced window-handling techniques in Playwright ensure your automation remains stable when dealing with multiple tabs, pop-ups, and dynamic page transitions. By waiting for specific load states and identifying the correct window context, you can reliably interact with complex multi-page workflows.
A Frame is an <iframe> or the main document that is part of a page. The frame can let you create a new experience inside the page. These new experiences are then accessible through interactions with the page. Every time you need to inspect the page, you can access the current frame tree using the frame.childFrames() and page.mainFrame() methods. (It’s not automatically displayed when the page loads.)
Three events on the page object regulate the lifecycle of the frame object:
When handling frames and windows in Playwright, you must pay special attention to iframe-based content, as elements inside frames cannot be accessed through the main page context. Properly handling iframes in Playwright ensures your automation script interacts with all nested and dynamic elements reliably.
To understand how to handle frames and windows in Playwright, let’s consider a practical test scenario.
Test Scenario:
Let's use https://letcode.in/frame to demonstrate how handling iframes in Playwright works. When you inspect this page, you can see multiple <iframe> tags embedded within the document.
Code Implementation:
import { expect, test } from "@playwright/test";
test("Interact with frames", async ({ page }) => {
await page.goto("https://letcode.in/frame");
const allframes = page.frames();
console.log("No.of frames: " + allframes.length);
})
Code Walkthrough:
Note that dynamic content, such as advertisements, may affect the frame count, so enter an approximate value in your test script.
Viewing Test Reports:
There are two ways to view the test results:


A page can have one or more frame objects attached via the <iframe> HTML tag. Each page has a main frame, and page-level interactions like click are assumed to work within the main frame. The <iframe> tag allows for the attachment of additional frames to a page.
To interact with elements inside these frames, you can access the frame objects. Frame objects can be obtained using the page.frame() API. You can also use page.frames() to get all frames or frame.childFrames() to access nested frames.
A page can have one or more frame objects attached to the iframe HTML tag. Each page has a main frame and page-level interactions like “click” that are assumed to work within the main frame.
For a practical demo on handling frames and windows in Playwright, we will use the same website as before, and interact with the “First Name” and “Last Name” fields.
Test Scenario:
Code Implementation:
import { expect, test } from "@playwright/test";
test("Interact with frames", async ({ page }) => {
await page.goto("https://letcode.in/frame");
const allframes = page.frames();
console.log("No.of frames: " + allframes.length);
const myFrame = page.frame("firstFr")
if (myFrame != null) {
await myFrame.fill("", "")
}
await myFrame?.fill("input[name='fname']", "yourname")
await myFrame?.fill("input[name='lname']", "yourexample")
expect(await myFrame?.locator("p.has-text-info").textContent()).toContain("You have entered")
})

Code Walkthrough:
fill function to pass the locator and test data.When you need to test iframes, you can use a frame locator, one of the Playwright locators, to access the iframe and interact with elements inside it. The frame locator helps you target elements within the frame efficiently.
Code Implementation:
test("Interact with frames", async ({ page }) => {
await page.goto("https://letcode.in/frame");
const allframes = page.frames();
console.log("No.of frames: " + allframes.length);
const frame = page.frameLocator("#firstFr")
await frame.locator("input[name='fname']").fill("Koushik");
await frame.locator("input[name='lname']").fill("Chatterjee");Code Walkthrough:
Nested frames occur when one iframe is embedded inside another iframe. This allows for more complex page layouts, as frames can be nested to multiple levels.
In modern web pages, <iframe> elements are used instead of the older <frameset> approach. Playwright provides tools like frame locators to interact with both parent and nested frames.
Code Implementation:
import { expect, test } from "@playwright/test";
test("Interact with frames", async ({ page }) => {
await page.goto("https://letcode.in/frame");
const allframes = page.frames();
console.log("No.of frames: " + allframes.length);
const frame = page.frameLocator("#firstFr")
await frame.locator("input[name='fname']").fill("Koushik");
await frame.locator("input[name='lname']").fill("Chatterjee");
const innerFrame = frame.frameLocator("iframe[src='innerFrame']")
await innerFrame.locator("input[name='email']").fill("koushik@gmail.com")
await frame.locator("input[name='fname']").fill("letcode");
const myFrame = page.frame("firstFr")
if (myFrame != null) {
await myFrame.fill("", "")
}
await myFrame?.fill("input[name='fname']", "koushik")
await myFrame?.fill("input[name='lname']", "chatterjee")
expect(await myFrame?.locator("p.has-text-info").textContent()).toContain("You have entered")
await page.waitForTimeout(3000);
Code Walkthrough:
Result:

Now that we’ve explored how to automate single and nested frames in Playwright, the next step is understanding how to handle full window interactions.
Playwright allows you to work within embedded content, but real-world applications often trigger entirely new tabs or pop-up windows.
Let’s move on and see how to handle windows in Playwright effectively.
Handling windows in Playwright is simple. Each browser can have multiple pages, and a Page refers to a single tab or a pop-up window within a browser context. You can use it to navigate URLs and interact with the page content.
Using the expect_page method, you can act on a new window or wait for it to load. Once the new window appears, you can use Playwright’s assertion library to ensure that your user journey is successful.
When handling windows in Playwright, it’s important to understand that your automation may involve not just a single window but also multiple tabs or additional pop-up windows that open during user actions. Playwright treats each of these as a separate page, and your test must be structured to detect and interact with them correctly.
Let’s understand how to handle multiple windows and tabs in Playwright in the sections below.
When working with web applications, certain actions, such as clicking a link, opening a pop-up, or triggering a new tab, can create additional browser windows.
Understanding how to handle multiple windows in Playwright ensures your tests remain stable and accurately simulate real user behavior.
To understand how this works, let’s consider a test scenario.
Test Scenario:
Code Implementation:
const [multiPage] = await Promise.all([
page.waitForEvent("popup"),
page.click("#followboth")
])
await multiPage.waitForLoadState();
const pages = multiPage.context().pages();
console.log('No.of tabs: ' + pages.length);
Code Walkthrough:
Handling tabs in Playwright is an essential part of handling windows in Playwright, especially when user actions open new browser tabs alongside pop-ups. Playwright allows you to detect, wait for, and interact with these tabs just like standalone pages, ensuring smooth workflows even when switching between multiple views. When combined with handling frames and windows in Playwright, this gives you full control over complex, multi-surface user journeys.
Test Scenario:
Code Implementation:
import { test } from "@playwright/test";
test("Handle tabs in Playwright", async ({ page }) => {
// Navigate to the demo page
await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal-demo");
console.log("Main page URL: " + page.url());
// Click the "Follow On Twitter" link and wait for new tab
const [newWindow] = await Promise.all([ page.waitForEvent("popup"), page.click("'Follow On Twitter'") ]);
// Log the URL of the new tab
console.log("New tab URL: " + newWindow.url());
});
Code Walkthrough:
Test Execution:
Open the terminal, and run the following command given below:
npx playwright test tests/windows.test.ts

Handling frames, iframes, windows, and tabs in Playwright locally can pose several challenges, such as browser inconsistencies, dynamic content like pop-ups or ads, and nested frames can lead to flaky tests.
Limited hardware resources make running multiple windows or tabs simultaneously difficult, and differences between local and production environments can cause unexpected failures. Observing complex test flows and debugging multi-window interactions also becomes cumbersome on a local setup.
Cloud-based platforms, such as LambdaTest, help overcome these challenges by providing multiple browser versions, operating systems, and screen resolutions for testing. They support scalable parallel execution, real-time debugging, and reliable network conditions, ensuring stable interactions with frames and windows.
With cloud testing, teams can efficiently run, monitor, and maintain complex Playwright automation scenarios, making tests faster, more reliable, and easier to manage.
LambdaTest is a GenAI-native test execution platform that allows you to perform manual and Playwright testing at scale across 3000+ browsers and OS combinations.
To get started with the LambdaTest platform, follow the steps below:
const capabilities = {
browserName: "Chrome",
browserVersion: "latest",
"LT:Options": {
platform: "Windows 10",
build: "Playwright Test from config",
name: "Playwright Windows Test",
user: process.env.LT_USERNAME,
accessKey: process.env.LT_ACCESS_KEY,
network: true,
video: true,
console: true
}
};
Use the LambdaTest Automation Capabilities Generator to create the capabilities block for any browser, OS, or configuration you need.
connectOptions: {
wsEndpoint: `wss://cdp.lambdatest.com/playwright?capabilities=${encodeURIComponent(
JSON.stringify(capabilities)
)}`
},Test Execution:

To get started, refer to the documentation on Playwright testing with LambdaTest.
Once you understand the basics of handling frames and windows in Playwright, the next level of complexity usually comes from managing multiple windows, tabs, and multi-page interactions. These advanced techniques help ensure your tests remain stable when dealing with dynamic browser behaviors such as pop-ups, external links, or multiple tabs.
When new windows or tabs open, Playwright may try to proceed before the pages finish loading. To prevent this, use waitForLoadState() after triggering actions like clicking the followboth button. This ensures every page reaches a reliable load state before your test continues.
Playwright supports three load states:
When opening new windows or pop-ups in Playwright, it’s important to wait until the page is fully ready before interacting with elements. Using the domcontentloaded load state ensures that the HTML is completely parsed and ready for actions, even if other resources like images or scripts are still loading. This is especially useful when automating multi-window workflows.
To understand the working of the domcontentloaded load state in Playwright for handling windows in Playwright, let's take a test scenario.
Test Scenario:
Code Implementation:
import { test } from "@playwright/test";
test("Handle windows with domcontentloaded load state", async ({ page }) => {
// Navigate to the test page
await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal");
// Trigger pop-ups and wait for new page event
const [multiPage] = await Promise.all([ page.waitForEvent("popup"), page.click("#followboth") ]);
// Wait until the new pages reach DOMContentLoaded
await multiPage.waitForLoadState("domcontentloaded");
// Retrieve all open pages
const pages = multiPage.context().pages();
console.log("No. of tabs: " + pages.length);
// Print URL of each open tab
pages.forEach(tab => console.log(tab.url()));
});
Code Walkthrough:
When automating multiple windows or pop-ups, you may need to ensure that all page resources, including images, scripts, stylesheets, and media, are completely loaded before interacting with the page. Using the load load state in Playwright ensures that the entire page and its associated resources are ready, making it ideal for resource-heavy or visually rich pages.
To understand the working of the load load state in Playwright for handling windows, let’s take a test scenario.
Test Scenario:
Code Implementation:
import { test } from "@playwright/test";
test("Handle windows with load state", async ({ page }) => {
// Navigate to the test page
await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal");
// Trigger pop-ups and wait for new page event
const [multiPage] = await Promise.all([ page.waitForEvent("popup"), page.click("#followboth") ]);
// Wait until the new pages are fully loaded (all resources)
await multiPage.waitForLoadState("load");
// Retrieve all open pages
const pages = multiPage.context().pages();
console.log("No. of tabs: " + pages.length);
// Print URL of each open tab
pages.forEach(tab => console.log(tab.url()));
});
Code Walkthrough:
When automating dynamic pages or multi-window workflows, some pages continue to load resources in the background such as AJAX calls or API requests. Using the networkidle load state ensures that all active network requests have finished and the page is completely idle before performing any further actions. This is very useful for dynamic web applications.
To understand the working of the networkidle load state in Playwright for handling windows in Playwright, let's take a test scenario.
Test Scenario:
Code Implementation:
import { test } from "@playwright/test";
test("Handle windows with networkidle load state", async ({ page }) => {
// Navigate to the test page
await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal");
// Trigger pop-ups and wait for new page event
const [multiPage] = await Promise.all([ page.waitForEvent("popup"), page.click("#followboth") ]);
// Wait until the new pages are idle (no network requests for 500ms)
await multiPage.waitForLoadState("networkidle");
// Retrieve all open pages
const pages = multiPage.context().pages();
console.log("No. of tabs: " + pages.length);
// Print URL of each open tab
pages.forEach(tab => console.log(tab.url()));
});
Code Walkthrough:
When working with multiple browser windows or tabs in Playwright, it becomes essential to identify which page corresponds to a specific URL and interact only with the required page. Playwright provides flexibility by allowing you to iterate through all open pages and selectively perform actions on the correct window, a key skill in accurate multi-window automation.
To understand how to interact with multiple web pages or frames in Playwright, let's consider the following test scenario.
Test Scenario:
Code Implementation:
import { test } from "@playwright/test";
test("Interact with multiple pages", async ({ page }) => {
// Navigate to the test page
await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal");
// Trigger pop-ups and wait for new page events
const [multiPage] = await Promise.all([
page.waitForEvent("popup"),
page.click("#followboth")
]);
// Retrieve all open pages
const pages = multiPage.context().pages();
console.log("No. of tabs: " + pages.length);
// Identify the Facebook page
let facebookPage;
for (let index = 0; index < pages.length; index++) {
const url = pages[index].url();
if (url === "https://www.facebook.com/lambdatest/") {
facebookPage = pages[index];
}
}
// Extract text from the Facebook page
const text = await facebookPage.textContent("//h1");
console.log(text);
});
Code Walkthrough:
Handling frames, windows, and multiple pages in Playwright is a crucial skill for automating complex web applications. Frames, including nested iframes, allow you to interact with embedded content that exists outside the main page context, and Playwright provides APIs like page.frame(), page.frames(), and frameLocator() to facilitate these interactions safely.
When dealing with new windows or tabs, Playwright treats each as a separate Page object, which can be captured using page.waitForEvent('popup') and interacted with just like any other page, ensuring seamless automation across multiple surfaces. Stability in multi-window and multi-frame workflows is enhanced by waiting for specific load states such as domcontentloaded, load, or networkidle, which guarantees that the page’s content and resources are fully available before any actions are performed.
By combining these techniques, testers can precisely target frames, navigate between tabs, and perform reliable interactions even on dynamic, resource-heavy, or nested page layouts. Overall, Playwright’s capabilities for handling frames and windows make it possible to create resilient and maintainable automation scripts that accurately replicate real user journeys, while cloud-based testing platforms further enhance scalability and consistency across different browsers and operating systems.
Did you find this page helpful?
More Related Hubs
Start your journey with LambdaTest
Get 100 minutes of automation test minutes FREE!!