Table Methods
Methods on the object returned by useTable(...).init().
const table = await useTable(page.locator('#my-table'), config).init();→ API: Config Options · API: SmartRow
Lifecycle
init
Signature
init(options?: { timeout?: number }): Promise<TableResult<T>>Parameters
options- Optional timeout for header resolution (default: 3000ms)
Resolves column headers and prepares the table for use. Must be called before any sync methods (getRow, getRowByIndex). Async methods auto-initialize if needed.
const table = await useTable(locator, config).init();
const table = await useTable(locator, config).init({ timeout: 5000 });isInitialized
Signature
isInitialized(): booleanReturns true if init() has completed successfully.
if (!table.isInitialized()) await table.init();isEmpty
Signature
isEmpty(): booleanReturns true when init() succeeded through the emptyState path: header resolution failed, but the configured empty-state locator was visible. Row operations still throw on an empty table, so check this before calling them.
await table.init();
if (table.isEmpty()) {
await expect(page.getByText('No results')).toBeVisible();
return;
}currentPageIndex
Signature
currentPageIndex: number0-based index of the page currently rendered in the DOM. Maintained by the library during pagination, bringIntoView(), and reset().
Treat it as read-only. Assigning it manually can desync the pagination path planner, and logs a warning (since v6.22.0). It may become read-only in v7.
await table.findRows({ Status: 'Active' }, { maxPages: 3 });
console.log(table.currentPageIndex); // e.g. 2reset
Signature
reset(): Promise<void>Resets table state (clears cached page position) and calls the onReset strategy if configured. Use after iteration to restore the table to its first page.
await table.reset();revalidate
Signature
revalidate(): Promise<void>Re-scans headers and rebuilds the column map without resetting pagination state. Use when columns change visibility or order dynamically (e.g., after toggling column visibility).
await table.revalidate();Row Access
getRow
Signature
getRow(
filters: Record<string, FilterValue>,
options?: { exact?: boolean }
): SmartRow<T>Finds a row on the current page only by column filters. Returns synchronously — does not paginate. Because the sync path can't compute a real index, the returned row's rowIndex is undefined (so bringIntoView() is limited); use findRow when you need a row with an accurate rowIndex.
const row = table.getRow({ Name: 'John Doe' });
const email = await row.getCell('Email').innerText();WARNING
getRow only searches the currently visible page. Use findRow to search across pages.
getRowByIndex
Signature
getRowByIndex(
index: number
): SmartRow<T>Parameters
index- 0-based position within the current render window
Gets a row by its 0-based position in the currently-rendered DOM — synchronous, no scroll.
For button-paginated tables this is the i-th row on the current page. For virtualized tables the render window shifts as you scroll, so getRowByIndex(i) returns whatever row currently sits at DOM position i, not the logical/absolute row i in the dataset once the list has scrolled. To iterate virtualized rows by their logical identity, use map / findRows with a dedupe strategy.
Resolution is lazy: an out-of-range index returns a SmartRow whose operations fail when it is used, rather than throwing here.
// i-th row currently rendered (e.g. row 0 of the current page)
const firstRow = table.getRowByIndex(0);findRowByIndex
Signature
findRowByIndex(
index: number,
options?: { maxPages?: number, signal?: AbortSignal }
): Promise<SmartRow<T>>Parameters
index- 0-based logical/data-model row indexoptions-maxPagesbounds how far to scroll/paginate (defaults to config.maxPages).
Returns the row with a specific logical (data-model) index, scrolling or paginating to reach it. Unlike getRowByIndex, which returns whatever sits at a DOM position, this finds the true row index in the dataset on virtualized tables.
The row is reached by, in order:
- a currently-mounted row whose logical index matches
strategies.viewport.scrollToRow(random-access jump), if configured- advancing pages (on infinite-scroll tables, a "page" is a scroll step), up to
maxPages
Requires strategies.resolveRowIndex; throws if it isn't configured. Also throws if the row can't be reached — it never falls back to a different row.
const row = await table.findRowByIndex(250, { maxPages: 50 });
await expect(row.getCell('ID')).toHaveText('250');findRow
Signature
findRow(
filters: Record<string, FilterValue>,
options?: { exact?: boolean, maxPages?: number, signal?: AbortSignal }
): Promise<SmartRow<T>>Parameters
filters- The filter criteria to matchoptions- Search options including exact match and max pages.signalcancels the page scan; the call rejects withsignal.reason.
Searches for a single matching row across pages. Paginates automatically. Returns the first match.
const row = await table.findRow({ Email: 'john@example.com' });
const row = await table.findRow({ Status: 'Active' }, { maxPages: 10 });findRows
Signature
findRows(
filters?: Record<string, FilterValue>,
options?: { exact?: boolean, maxPages?: number, useBulkPagination?: boolean, signal?: AbortSignal }
): Promise<SmartRowArray<T>>Parameters
filters- The filter criteria to match (omit or pass {} for all rows)options- Search options (exact, maxPages).useBulkPaginationdefaults tofalse: pages advance one at a time viagoNextso no intermediate page is skipped. Set it totrueto opt intogoNextBulk(faster, but skips the rows on jumped-over pages).signalcancels the page scan; the call rejects withsignal.reason.
Searches for all matching rows across pages. Pass empty filters {} or omit to collect every row.
Returned rows can be read individually, sequentially, or with different column selections, even when the table's concurrency is 'synchronized'. The array's toJSON() helper reads rows sequentially to avoid competing viewport moves. For coordinated column scrolling across virtualized rows, use map(({ row }) => row.toJSON(), { concurrency: 'synchronized' }).
const activeRows = await table.findRows({ Status: 'Active' });
const allRows = await table.findRows();Iteration
forEach
Signature
forEach(
callback: (ctx: RowIterationContext<T>) => void | Promise<void>,
options?: RowIterationOptions
): Promise<void>Parameters
callback- Function receivingoptions- maxPages, concurrency, dedupe, useBulkPagination
Iterates every row across all pages. Runs sequentially by default. Call stop() in the callback to halt early. In sequential mode no rows after the one that called it are visited; in parallel mode the rest of the current page may still run.
await table.forEach(async ({ row, stop }) => {
const status = await row.getCell('Status').innerText();
if (status === 'Archived') stop();
await row.getCell('Checkbox').click();
});map
Signature
map<R>(
callback: (ctx: RowIterationContext<T>) => R | Promise<R>,
options?: RowIterationOptions
): Promise<R[]>Parameters
callback- Function receivingoptions- maxPages, concurrency, dedupe, useBulkPagination
Transforms every row across all pages into a value. Runs sequentially by default (since v6.22.0), which is safe for callbacks that click, fill, or open popovers. Pass concurrency: 'parallel' for read-only extraction when speed matters.
// Default (sequential) — safe for UI interactions
const results = await table.map(async ({ row }) => {
await row.getCell('Actions').locator('button').click();
return page.locator('.dialog .title').innerText();
});
// Read-only — opt into parallel
const emails = await table.map(
({ row }) => row.getCell('Email').innerText(),
{ concurrency: 'parallel' }
);toArray
Signature
toArray<R = Record<string, unknown>>(
callback?: (ctx: RowIterationContext<T>) => R | Promise<R>,
options?: RowIterationOptions
): Promise<R[]>Parameters
callback- Optional map function. Defaults to({ row }) => row.toJSON().options- Same options asmap().
Shorthand for map() followed by reset(). Iterates every row across all pages, applies the callback (defaults to row.toJSON()), then resets the table to page 1 — even if the callback throws. Accepts the same options as map.
// Every row as JSON
const rows = await table.toArray();
// Custom callback
const names = await table.toArray(({ row }) => row.getCell('Name').innerText());filter
Signature
filter(
predicate: (ctx: RowIterationContext<T>) => boolean | Promise<boolean>,
options?: RowIterationOptions
): Promise<SmartRowArray<T>>Parameters
predicate- Function receivingoptions- maxPages, concurrency, dedupe, useBulkPagination
Collects rows matching an async predicate across all pages. Returns a SmartRowArray.
const highEarners = await table.filter(async ({ row }) => {
const salary = await row.getCell('Salary').innerText();
return parseInt(salary.replace(/\D/g, '')) > 50000;
});Async iteration
The table implements AsyncIterable, so you can use for await...of for fine-grained control with break:
for await (const { row, rowIndex } of table) {
if (await row.getCell('Status').innerText() === 'Archived') break;
}Column Utilities
getHeaders
Signature
getHeaders(): Promise<string[]>Returns the resolved column header names in order.
const headers = await table.getHeaders();
// ['Name', 'Email', 'Status', ...]getHeaderCell
Signature
getHeaderCell(columnName: string): Promise<Locator>Returns a Locator for the header cell of a named column.
const nameHeader = await table.getHeaderCell('Name');
await nameHeader.click(); // sort by NamescrollToColumn
Signature
scrollToColumn(columnName: string): Promise<void>Scrolls horizontally to bring a column into view. Prefers strategies.viewport.scrollToColumn when configured; otherwise scrolls the header cell with Playwright's scrollIntoViewIfNeeded().
await table.scrollToColumn('Notes');countRows
Signature
countRows(filters?: Record<string, FilterValue>, options?: { exact?: boolean; maxPages?: number; signal?: AbortSignal }): Promise<number>Returns the number of rows currently visible on the page. Does not paginate.
const count = await table.countRows();mapColumn
Signature
mapColumn<R = string>(columnName: string, options?: RowIterationOptions): Promise<R[]>Parameters
columnName- The name of the column to extractoptions- Iteration options
Extracts all values for a single column across pages. More efficient than map + toJSON for single-column reads.
const statuses = await table.mapColumn('Status');
const counts = await table.mapColumn<number>('Count');getColumnValues
Deprecated — use
mapColumn(ormap) instead. Will be removed in v7.0.0.
Signature
getColumnValues(columnName: string, options?: RowIterationOptions): Promise<string[]>Parameters
columnName- The name of the column to extractoptions- Iteration options
Extracts all values for a single column as strings. Convenience wrapper around mapColumn.
// Preferred:
const names = await table.mapColumn('Name');
// Deprecated:
const namesLegacy = await table.getColumnValues('Name');Sorting
sorting.apply
Signature
sorting: {
apply(columnName: string, direction: 'asc' | 'desc'): Promise<void>;
getState(columnName: string): Promise<'asc' | 'desc' | 'none'>;
}Parameters
columnName- The name of the column to check.
Applies the configured sorting strategy to a column.
await table.sorting.apply('Name', 'asc');
await table.sorting.apply('Created At', 'desc');Diagnostics
generateConfig
Signature
generateConfig(): Promise<void>Dumps table HTML and TypeScript type definitions to help generate PST configuration. Intentionally throws an error containing the prompt — copy the output and pass it to an AI assistant.
await table.generateConfig();