Skip to content

Config Options ​

All options passed to useTable(locator, config). Every option is optional unless noted.

typescript
import { useTable } from '@rickcedwhat/playwright-smart-table';

const table = await useTable(page.locator('#my-table'), {
  rowSelector: 'tbody tr',
  maxPages: 20,
  strategies: { ... },
}).init();

→ API: Table Methods · API: Strategies


Selectors ​

All three selectors share one type:

typescript
type TableSelector = string | ((parent: Locator) => Locator)

A string is resolved relative to the parent; a function receives the parent Locator — the table root for headers and rows, the row for cells.

headerSelector ​

Signature ​

typescript
headerSelector?: TableSelector

Selector for the column header elements, relative to the table root. Accepts a CSS string or a function that receives the root Locator. Default: 'thead th'.

typescript
// CSS string
headerSelector: 'thead th'

// Locator function
headerSelector: (root) => root.locator('[role="columnheader"]')

// Headers rendered as a separate table outside the root
headerSelector: (root) => root.page().locator('#header-table th')

String-only features

The dataAttribute viewport strategy reads headers in the browser and needs a CSS string for scrollToColumn. Given a function, it logs a one-time warning and skips the horizontal scroll. The MUI DataGrid preset falls back to .MuiDataGrid-columnHeader.

→ Guide: Identify Your Table


rowSelector ​

Signature ​

typescript
rowSelector?: TableSelector

Selector for the table rows, relative to the table root. Accepts a CSS string or a function that receives the root Locator. Default: 'tbody tr'.

typescript
rowSelector: 'tbody tr'
rowSelector: '[role="row"].data-row'

// Locator function — e.g. skip group-header rows
rowSelector: (root) => root.locator('tbody tr').filter({ hasNot: root.page().locator('td[colspan]') })

String-only features

The dataAttribute viewport strategy measures rows in the browser and needs a CSS string. Given a function, it logs a one-time warning, treats every mounted row as possibly visible (no overscan filtering, no row-range shortcut), and skips scrollToRow. The MUI DataGrid preset falls back to [data-rowindex] for scrolling and ranges, and only loses overscan filtering.

→ Guide: Identify Your Table


cellSelector ​

Signature ​

typescript
cellSelector?: TableSelector

Selector for cells within a row. Accepts a CSS string or a function that receives the row Locator. Default: 'td'.

typescript
// CSS string
cellSelector: 'td'

// Locator function
cellSelector: (row) => row.locator('[role="gridcell"]')

→ Guide: Identify Your Table


Behavior ​

maxPages ​

Signature ​

typescript
maxPages?: number

Maximum number of pages to scan during iteration and search operations. Prevents runaway pagination on unexpectedly large tables.

Defaults to 1. If you configure a pagination strategy, set this explicitly or iteration and search will never leave page 1 — init() logs a warning when it sees a pagination strategy with maxPages: 1.

typescript
maxPages: 50

concurrency ​

Signature ​

typescript
concurrency?: RowIterationMode

Default concurrency mode for forEach, map, filter, and toArray. Can be overridden per-call.

  • 'sequential' — one row at a time, in order. Default for all iteration methods (since v6.22.0).
  • 'parallel' — all rows on the current page run concurrently. Faster for read-only callbacks.
  • 'synchronized' — rows run in parallel but page navigation waits for all callbacks to finish.
typescript
concurrency: 'parallel'

→ Guide: Iterate Rows


autoScroll ​

Signature ​

typescript
autoScroll?: boolean

When true, PST scrolls the table locator into view during init(). Useful when tables render below the fold.

typescript
autoScroll: true

Hooks ​

headerTransformer ​

Signature ​

typescript
headerTransformer?: (args: { text: string, index: number, locator: Locator, seenHeaders: Set<string> }) => string | Promise<string>

Rename or normalize column headers at init time. Runs once per header cell during init().

typescript
headerTransformer: ({ text, index }) => text.trim() || `Column ${index}`
typescript
// Rename a known system column
headerTransformer: ({ text }) =>
  text.includes('__checkbox__') ? 'Select' : text.trim()

→ Guide: Custom Header Text


onReset ​

Signature ​

typescript
onReset?: (context: TableContext) => Promise<void>

Called after table.reset(). Use to navigate back to the first page, clear filters, or perform any teardown the table requires.

typescript
onReset: async ({ root, page }) => {
  await page.locator('.pagination-first').click();
}

Advanced ​

columnOverrides ​

Signature ​

typescript
columnOverrides?: Partial<Record<keyof T, ColumnOverride<T[keyof T]>>>

Per-column read and write overrides. Use read to customize how cell text is extracted; use write to customize how values are filled.

typescript
columnOverrides: {
  Status: {
    read: async (cell) => cell.locator('.badge').innerText(),
    write: async ({ cell, targetValue }) => {
      await cell.locator('select').selectOption(targetValue);
    },
  },
}

→ Guide: Column Overrides · Guide: Fill Cells


syntheticColumns ​

Signature ​

typescript
syntheticColumns?: Record<string, SyntheticColumnDef<T>>

Computed columns with no DOM presence. Each key becomes a virtual column whose value comes from compute(row).

Synthetic columns are available in toJSON(), row.getValue(), getHeaders(), and findRow / findRows / countRows filters. They are not available in:

  • getRow() filters — use findRow() (synthetics need async evaluation)
  • row.getCell() or smartFill() — there's no cell to return or fill

compute may read real columns and columnOverrides, but not other synthetic columns.

typescript
syntheticColumns: {
  Total: {
    compute: async (row) => {
      const price = Number(await row.getValue('Price'));
      const qty = Number(await row.getValue('Qty'));
      return price * qty;
    },
  },
}

// Computed values are stringified before matching
const row = await table.findRow({ Total: '40' }, { exact: true });

→ Guide: Column Overrides


emptyState ​

Signature ​

typescript
emptyState?: Locator

Locator for the element that replaces the table when there are no results. If header resolution fails during init(), a visible configured emptyState locator lets init() succeed and isEmpty() returns true; otherwise, init() throws the header resolution error. A table with resolvable headers and zero rows can initialize successfully without emptyState.

Row lookup operations still throw when isEmpty() is true — check it first.

typescript
const table = await useTable(page.locator('#orders'), {
  emptyState: page.getByText('No orders found'),
}).init();

if (table.isEmpty()) return;

debug ​

Signature ​

typescript
debug?: DebugConfig

Development aids: internal logging and slow-motion delays. Remove before committing — debug.slow warns when it detects CI.

  • logLevel — 'none' (default) · 'error' · 'info' · 'verbose'
  • slow — delay in ms for every operation, or per type: { pagination, getCell, findRow, default }
typescript
debug: { logLevel: 'verbose' }

debug: { logLevel: 'info', slow: { pagination: 500 } }

strategies ​

Signature ​

typescript
strategies?: TableStrategies

All pluggable strategy overrides — pagination, loading detection, header discovery, sorting, viewport navigation, and more.

typescript
strategies: {
  pagination: PaginationStrategies.infiniteScroll({ ... }),
  loading: { isCellLoading: async (cell) => ... },
}

→ API: Strategies