Skip to content

Iterate Rows ​

Walk every row ​

typescript
await table.forEach(async ({ row }) => {
  await expect(row.getCell('Status')).toHaveText('Active')
})

Processes rows one at a time, in order. Default for forEach.


Map rows to data ​

typescript
const emails = await table.map(async ({ row }) => row.getCell('Email').innerText())
const allRows = await table.map(async ({ row }) => row.toJSON())

Like forEach but returns an array. Runs one row at a time by default — if you're only reading, pass { concurrency: 'parallel' } to speed it up.


Filter rows by a condition ​

typescript
const highEarners = await table.filter(
  async ({ row }) => {
    const salary = await row.getCell('Salary').innerText()
    return parseInt(salary.replace(/[^0-9]/g, '')) > 50000
  },
  { maxPages: 5 }
)

Returns a SmartRowArray of all rows where the predicate returns true. Default concurrency for filter is sequential.


Early exit ​

Call stop() from the callback to halt iteration early:

typescript
await table.forEach(async ({ row, stop }) => {
  const status = await row.getCell('Status').innerText()
  if (status === 'Archived') stop()
})

stop() works in forEach, map, and filter. No further pages are loaded, and map / filter results include the row that called stop() and nothing after it. If several rows call it, the earliest one wins.

What happens to the rest of the current page depends on concurrency:

  • 'sequential' (default): rows run one at a time, so no callback after the stopping row runs.
  • 'parallel' / 'synchronized': the whole page is already in flight, so later rows' callbacks may still run and their side effects (clicks, fills) still happen. Their results are dropped.

If you need side effects to stop at an exact row, use the default sequential mode, or iterate with for await, which visits one row at a time and supports break:

typescript
for await (const { row } of table) {
  if (await row.getCell('Status').innerText() === 'Archived') break
}

Concurrency ​

forEach, map, and filter all accept a concurrency option:

typescript
await table.map(async ({ row }) => row.toJSON(), { concurrency: 'parallel' })
  • sequential — one row at a time, in order. The default for everything, unless you set concurrency in your table config — then that's the default, and a per-call option still wins.
  • parallel — all row callbacks on a page run concurrently. Fastest, but only safe when your callback just reads.
  • synchronized — row callbacks run in parallel, but the library waits for all rows on the current page to finish before advancing to the next page. Use when your callbacks interact with the table and page navigation needs to be coordinated.

Options ​

All three methods accept a second options argument:

typescript
await table.map(
  async ({ row }) => row.toJSON(),
  {
    maxPages: 5,
    dedupe: async (row) => row.getCell('ID').innerText(),
  }
)
  • maxPages — cap how many pages are visited. Defaults to config.maxPages.
  • dedupe — for tables that replay records as you scroll or paginate, return a unique, stable record key (such as the ID above). Already-seen keys are skipped. Screen position, a mounted-slot index, and page-local indices can collide for different records. Strategies.Dedupe.byTopPosition() is deprecated; see dedupe guidance.

The callback also receives index (0-based visit counter) and pageIndex (which page the row came from). See the API reference for the full options list.


→ API Reference: Table Methods — forEach · Table Methods — map · Table Methods — filter