Dynamic tables in Playwright Java are common in modern web applications, and they often change based on sorting, filtering, or live updates. Because the content does not stay static, testers need a clear approach to locate rows, read values, and interact with elements inside these tables. In this guide, you will learn practical ways to work with different types of dynamic tables using structured examples and easy steps.
Right after this, you can also explore how dropdowns work in Playwright Java for a better understanding of element interactions.

- What Are Dynamic Web Tables in Playwright Java
- Download Practice HTML File for Dynamic Table Examples
- Locating Table Elements in Playwright Java
- Read Table Data: Get Rows and Columns
- Handle Pagination in Dynamic Tables
- Handle Table Sorting and Filtering
- Strategies to Handle Changing Table Structures
- How to Extract Complete Table Data in Playwright Java
- Sorting Table Data in Playwright Java
- Conclusion
What Are Dynamic Web Tables in Playwright Java
Dynamic web tables are tables whose content changes based on user actions or data updates. These tables may load new rows when filters are applied, update values after sorting, or shift content when pagination is used. Because their structure often looks stable but their data changes frequently, automating them requires an approach that focuses on reliable locators and careful synchronization.
Most dynamic tables rely on asynchronous loading, which means the rows may not appear instantly. For this reason, using proper waits and checking the table state before interacting with it becomes important. Understanding how these tables behave will help you read data, verify values, and interact with elements inside the rows more accurately.
Download Practice HTML File for Dynamic Table Examples
To help you practice each example in this guide, you can download a ready-to-use HTML file that contains a dynamic web table with sorting, filtering, and pagination.
Download HTML file: dynamic-table-demo.html
All the Playwright Java examples in this tutorial use this file, so you can follow along step by step.

You can save the file as dynamic-table-demo.html on your computer and then load it in your Playwright scripts with a simple page.navigate() statement.
Using a reusable demo file makes it easier to try out table locators, extract row and column values, and automate sorting, filtering, and pagination.
Locating Table Elements in Playwright Java
Before interacting with any dynamic table, the first step is choosing stable and meaningful locators. Many tables use changing row numbers, dynamic classes, or hidden columns, so relying on elements that stay consistent helps avoid flaky tests.
You can often start by identifying the table container using an id or a unique attribute. After that, you can locate rows and columns through simple CSS selectors or structured XPath queries when the markup requires deeper navigation. Both approaches work well as long as the locators are tied to elements that do not change when the table updates.

When dealing with dynamic content, it also helps to target specific text inside a row or use partial matches to locate cells. This allows you to interact with the right row even when the table reloads new data. Choosing the right locator strategy early will make later steps like reading rows, verifying values or clicking elements much more reliable.
Read Table Data: Get Rows and Columns
Once the table is identified, the next step is reading its rows and cells in a reliable way. Dynamic tables often refresh their content, so it is important to wait until the table is fully visible before extracting any values. After that, you can use Playwright Java methods to collect row elements and then loop through them to access individual cells.
You can read table data by first capturing all visible rows, then locating the column elements inside each row. This helps you extract text, verify values or compare data across multiple rows. When working with large tables, keeping row and column selection simple improves accuracy and readability.
Here is a basic example of getting table data:
Locator table = page.locator("table#productTable");
Locator rows = table.locator("tbody tr");
int rowCount = rows.count();
for (int i = 0; i < rowCount; i++) {
Locator row = rows.nth(i);
Locator cells = row.locator("td");
int cellCount = cells.count();
for (int j = 0; j < cellCount; j++) {
String value = cells.nth(j).innerText();
System.out.println("Cell value: " + value);
}
}This pattern works well for most cases where the table updates but follows a predictable structure. It gives you clean access to all rows and columns without relying on unstable attributes.
Using XPath for dynamic rows and columns
If you want to master XPath for dynamic row indexing and complex table conditions, you can follow our dedicated XPath tutorial.
Handle Pagination in Dynamic Tables
Many dynamic tables display only a limited number of rows per page and load more content when the user clicks a pagination control. To automate these tables, you need a clear way to move through each page and gather or verify data across all of them.
You can start by identifying the pagination buttons or links, such as Next, Previous or numbered page controls. After clicking a control, it is important to wait for the table to refresh before reading new rows. This ensures that Playwright interacts only with the updated content.
Here is a simple example that loops through pages:
Locator nextButton = page.locator("#next");
Locator tableRows = page.locator("table#productTable tbody tr");
while (nextButton.isEnabled()) {
int rowCount = tableRows.count();
for (int i = 0; i < rowCount; i++) {
String text = tableRows.nth(i).innerText();
System.out.println("Row: " + text);
}
nextButton.click();
page.waitForLoadState();
}This pattern gives you a consistent way to collect data across all pages, validate row content or gather information for reporting. Handling pagination this way keeps the flow simple and reduces issues caused by partial or delayed table updates.
Handle Table Sorting and Filtering
Sorting and filtering are common features in dynamic tables, and they often cause the visible rows to change instantly. To work with these actions in Playwright Java, start by identifying the column headers or filter fields that trigger the update. After clicking a sort icon or entering a filter value, it is important to wait for the table to reload so that you interact only with the updated content.
For sorting, you can click the header element and then read the rows again to confirm the new order. For filtering, enter the filter text or select a filter option and then capture the refreshed rows to verify that only matching records are visible.
Here is a simple example of applying a filter:
page.fill("input#search", "Laptop");
page.keyboard().press("Enter");
// Wait for table to refresh
page.waitForSelector("table#productTable tbody tr");
// Read updated rows
Locator rows = page.locator("table#productTable tbody tr");
for (int i = 0; i < rows.count(); i++) {
System.out.println(rows.nth(i).innerText());
}This approach helps you validate that sorting or filtering actions are working correctly and that the table displays the expected results. Handling these interactions carefully keeps your tests accurate and reduces flaky behavior.
Strategies to Handle Changing Table Structures
Dynamic tables often change their layout based on user actions, backend updates or UI conditions. Columns may appear or disappear, row counts may increase, or certain cells may load only after a delay. To automate these variations in Playwright Java, it helps to use flexible locators and simple logic that adapts to different structures.
A good starting point is identifying rows and columns using parent child relationships rather than relying on fixed column indexes. This approach allows your test to continue working even when additional columns are added or the table order changes. You can also wait for essential elements such as tbody or at least one row to appear before interacting with the table. This ensures that updates such as filtering, sorting or pagination are completed before accessing new data.
Here is an example of reading column headers dynamically:
Locator headers = page.locator("table#productTable thead th");
// Get total number of columns
int headerCount = headers.count();
// Print each column header
for (int i = 0; i < headerCount; i++) {
String headerText = headers.nth(i).innerText();
System.out.println("Header: " + headerText);
}
// Print total column count
System.out.println("Total number of columns: " + headerCount);This pattern helps you understand the structure of the table at runtime and adjust your logic accordingly. It makes your automation more stable and reduces failures caused by layout changes or dynamic content. If you need to validate or extract specific values, combining dynamic header detection with simple row matching gives you a reliable way to work with tables that frequently change.
How to Extract Complete Table Data in Playwright Java
When working with tables, you will often need to extract all rows and columns together. This helps you verify the entire dataset or compare values during assertions. Playwright Java makes this task simple because you can read the table structure using locators.
Below is a clear and easy example to extract full table data across multiple rows and columns.
Example: Read All Table Rows and Columns
This code reads each row, then loops through each column to print the complete table data.
Locator rows = page.locator("table#productTable tbody tr");
int rowCount = rows.count();
System.out.println("Total rows: " + rowCount);
for (int i = 0; i < rowCount; i++) {
Locator row = rows.nth(i);
Locator cells = row.locator("td");
int cellCount = cells.count();
System.out.print((i + 1) + ": ");
for (int j = 0; j < cellCount; j++) {
String cellValue = cells.nth(j).innerText();
System.out.print(cellValue + " | ");
}
System.out.println();
}What this code does
- Finds all table rows inside
<tbody> - For each row, it locates all
<td>cells - Prints all cell values in a readable format
Example Output
Total rows: 4
1: Laptop | 800 | Electronics |
2: Headphone | 50 | Electronics |
3: Shoes | 40 | Fashion |
4: Watch | 120 | Fashion | This output helps you understand how Playwright sees the table data.
Extract Table Data into a Java List
Instead of printing values, you can also store the data in a list for assertions or further use in your automation framework.
Store rows in a List of Lists
List<List<String>> tableData = new ArrayList<>();
Locator rows = page.locator("table#productTable tbody tr");
int rowCount = rows.count();
for (int i = 0; i < rowCount; i++) {
List<String> rowData = new ArrayList<>();
Locator cells = rows.nth(i).locator("td");
for (int j = 0; j < cells.count(); j++) {
rowData.add(cells.nth(j).innerText());
}
tableData.add(rowData);
}
// Print complete table data
System.out.println(tableData);Use cases
- Validate backend API vs UI table values
- Compare two tables
- Export table data into Excel or CSV
- Use data-driven test validation
Extract Data by Column Name
If you want data from only one column, for example, Price or Stock, this method is more convenient.
Example: Read values under the Price column
Locator rows = page.locator("#table-body tr"); // select all rows
int rowCount = rows.count();
System.out.println("Total rows: " + rowCount);
for (int i = 0; i < rowCount; i++) {
// nth(1) because Price is the 2nd column
String price = rows.nth(i).locator("td").nth(1).innerText();
System.out.println("Price: " + price);
}Why this works
nth-child(1) selects the second column for every row.
Extract Data by Searching within Rows
This method helps when you want a specific row based on a value, like a product name.
Example: Find the row where Name is Laptop
Locator rows = page.locator("#table-body tr"); // all rows
int rowCount = rows.count();
for (int i = 0; i < rowCount; i++) {
Locator cells = rows.nth(i).locator("td");
String name = cells.nth(0).innerText(); // 0th index for Name
if (name.equalsIgnoreCase("Laptop")) {
System.out.println("Laptop row found: ");
for (int j = 0; j < cells.count(); j++) {
System.out.print(cells.nth(j).innerText() + " | ");
}
System.out.println(); // newline after printing row
}
}Common Assertions on Table Data
You can assert table data using TestNG assertions. Here are a few examples.
Assert row count
Assert.assertEquals(4, page.locator("table#productTable tbody tr").count());Assert a specific cell value
String price = page.locator("table#productTable tbody tr:nth-child(1) td:nth-child(2)").innerText();
Assert.assertEquals("800", price);Assert that a column contains a value
Assertions.assertTrue(page.locator("td:nth-child(2)").allInnerTexts().contains("Shoes"));Sorting Table Data in Playwright Java
Sorting is a common feature in dynamic tables. Automating it in Playwright Java allows you to verify that clicking a column header rearranges the table rows correctly. In dynamic-table-demo.html you can sort the table by Product Name, Price, or Category by clicking the respective <th> header.
Example: Sort by Column and Read Table Data
Here is a simple Playwright Java example to click the Price column header and print the sorted rows.
// Locate the Price column header
Locator priceHeader = page.locator("th[data-sort='price']");
// Click header to sort by Price
priceHeader.click();
// Wait for table rows to update (optional if table updates instantly)
page.waitForSelector("#table-body tr");
// Read all rows and print Product Name and Price
Locator rows = page.locator("#table-body tr");
int rowCount = rows.count();
System.out.println("Table sorted by Price:");
for (int i = 0; i < rowCount; i++) {
Locator cells = rows.nth(i).locator("td");
String name = cells.nth(0).innerText();
String price = cells.nth(1).innerText();
System.out.println(name + " | " + price);
}Output Example:
Table sorted by Price:
Mouse | 20
Keyboard | 25
Sunglasses | 30
Backpack | 35Sorting by Multiple Columns
If you want to sort first by Category and then by Price:
// Sort by Category
page.locator("th[data-sort='category']").click();
page.waitForSelector("#table-body tr");
// Sort by Price within the category
page.locator("th[data-sort='price']").click();
page.waitForSelector("#table-body tr");
// Print sorted data
Locator rows = page.locator("#table-body tr");
for (int i = 0; i < rows.count(); i++) {
Locator cells = rows.nth(i).locator("td");
System.out.println(cells.nth(0).innerText() + " | " +
cells.nth(1).innerText() + " | " +
cells.nth(2).innerText());
}This approach helps validate combined sorting behavior in dynamic tables.
Verify Sorted Data Programmatically
Instead of just printing, you can assert that the table is sorted correctly.
List<Integer> prices = new ArrayList<>();
Locator rows = page.locator("#table-body tr");
for (int i = 0; i < rows.count(); i++) {
String priceText = rows.nth(i).locator("td").nth(1).innerText();
prices.add(Integer.parseInt(priceText));
}
// Verify ascending order
for (int i = 0; i < prices.size() - 1; i++) {
if (prices.get(i) > prices.get(i + 1)) {
System.out.println("Table is not sorted correctly!");
}
}
System.out.println("Table sorted correctly by Price.");
This ensures that your automated test validates the actual sorted order.
If you want to improve your CSS based element targeting skills, you can explore more patterns here.
Conclusion
Working with tables in Playwright Java becomes simple once you understand how to locate cells, extract row data, handle sorting, apply filters, and manage pagination. In this guide, you learned step by step how to automate every common table operation using clear examples. Each example used the dynamic-table-demo.html file, so you can practice everything on your own system without relying on a live website.
As a result, you now have the skills to test real-world web tables that include search boxes, sortable headers, dynamic rows, and multiple pages of data. These techniques are useful when you validate dashboards, admin panels, product lists, employee records, order reports, and many other data-driven web pages.
If you follow the examples and customize them based on your project, you will be able to write stable and reliable Playwright Java test scripts for any table structure. Keep experimenting with the demo HTML file and continue exploring other Playwright features to strengthen your automation skills.