From f522d7f8ac04ced5477d654512f09215f0a71d17 Mon Sep 17 00:00:00 2001 From: cipherprofessor Date: Wed, 23 Sep 2026 18:12:27 +0530 Subject: [PATCH 1/4] fix(table-core): clamp out-of-range pageIndex when autoResetPageIndex is disabled Fixes #4994. --- .../clamp-page-index-when-reset-disabled.md | 5 + docs/framework/alpine/guide/pagination.md | 2 +- docs/framework/angular/guide/pagination.md | 2 +- docs/framework/ember/guide/pagination.md | 2 +- docs/framework/lit/guide/pagination.md | 2 +- docs/framework/octane/guide/pagination.md | 2 +- docs/framework/preact/guide/pagination.md | 2 +- docs/framework/react/guide/pagination.md | 2 +- docs/framework/solid/guide/pagination.md | 2 +- docs/framework/svelte/guide/pagination.md | 2 +- docs/framework/vue/guide/pagination.md | 2 +- .../rowPaginationFeature.types.ts | 2 + .../rowPaginationFeature.utils.ts | 25 ++- .../implementation/core/autoReset.test.ts | 162 +++++++++++++++++- 14 files changed, 198 insertions(+), 16 deletions(-) create mode 100644 .changeset/clamp-page-index-when-reset-disabled.md diff --git a/.changeset/clamp-page-index-when-reset-disabled.md b/.changeset/clamp-page-index-when-reset-disabled.md new file mode 100644 index 0000000000..838696aa95 --- /dev/null +++ b/.changeset/clamp-page-index-when-reset-disabled.md @@ -0,0 +1,5 @@ +--- +'@tanstack/table-core': patch +--- + +Clamp an out-of-range `pageIndex` to the last page when rows shrink and `autoResetPageIndex` (or `autoResetAll`) is `false` with client-side pagination, instead of leaving the table on an empty page diff --git a/docs/framework/alpine/guide/pagination.md b/docs/framework/alpine/guide/pagination.md index 8c0d12b7d6..c4b8bb3e0a 100644 --- a/docs/framework/alpine/guide/pagination.md +++ b/docs/framework/alpine/guide/pagination.md @@ -231,7 +231,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/angular/guide/pagination.md b/docs/framework/angular/guide/pagination.md index ee7e6ef51c..91a4b76507 100644 --- a/docs/framework/angular/guide/pagination.md +++ b/docs/framework/angular/guide/pagination.md @@ -321,7 +321,7 @@ readonly table = injectTable(() => ({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/ember/guide/pagination.md b/docs/framework/ember/guide/pagination.md index f4e7762059..1766da55ce 100644 --- a/docs/framework/ember/guide/pagination.md +++ b/docs/framework/ember/guide/pagination.md @@ -216,7 +216,7 @@ const table = useTable(() => ({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/lit/guide/pagination.md b/docs/framework/lit/guide/pagination.md index e465c3120c..9b1e6ee3f6 100644 --- a/docs/framework/lit/guide/pagination.md +++ b/docs/framework/lit/guide/pagination.md @@ -230,7 +230,7 @@ const table = this.tableController.table({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/octane/guide/pagination.md b/docs/framework/octane/guide/pagination.md index c443c974cd..9c5a8aec5e 100644 --- a/docs/framework/octane/guide/pagination.md +++ b/docs/framework/octane/guide/pagination.md @@ -216,7 +216,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/preact/guide/pagination.md b/docs/framework/preact/guide/pagination.md index a5b6007600..dc275c5ec4 100644 --- a/docs/framework/preact/guide/pagination.md +++ b/docs/framework/preact/guide/pagination.md @@ -302,7 +302,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/react/guide/pagination.md b/docs/framework/react/guide/pagination.md index ea4dce856b..e9eed2d702 100644 --- a/docs/framework/react/guide/pagination.md +++ b/docs/framework/react/guide/pagination.md @@ -299,7 +299,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/solid/guide/pagination.md b/docs/framework/solid/guide/pagination.md index 0f74f04c50..432acd7654 100644 --- a/docs/framework/solid/guide/pagination.md +++ b/docs/framework/solid/guide/pagination.md @@ -319,7 +319,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/svelte/guide/pagination.md b/docs/framework/svelte/guide/pagination.md index f4a065ae32..deae567002 100644 --- a/docs/framework/svelte/guide/pagination.md +++ b/docs/framework/svelte/guide/pagination.md @@ -328,7 +328,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/vue/guide/pagination.md b/docs/framework/vue/guide/pagination.md index aa61115f3a..16ebcef252 100644 --- a/docs/framework/vue/guide/pagination.md +++ b/docs/framework/vue/guide/pagination.md @@ -329,7 +329,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -Be aware, however, that if you turn off `autoResetPageIndex`, you may need to add some logic to handle resetting the `pageIndex` yourself to avoid showing empty pages. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts b/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts index a713cb8016..f063bdebe7 100644 --- a/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts +++ b/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts @@ -18,6 +18,8 @@ export interface TableState_RowPagination { export interface TableOptions_RowPagination { /** * If set to `true`, pagination will be reset to the first page when page-altering state changes e.g. `data` is updated, filters change, grouping changes, etc. + * + * If set to `false` with client-side pagination, the current page is kept, but a page index that no longer exists after the rows shrink is clamped to the last page. */ autoResetPageIndex?: boolean /** diff --git a/packages/table-core/src/features/row-pagination/rowPaginationFeature.utils.ts b/packages/table-core/src/features/row-pagination/rowPaginationFeature.utils.ts index 642561f75f..0f608582c6 100644 --- a/packages/table-core/src/features/row-pagination/rowPaginationFeature.utils.ts +++ b/packages/table-core/src/features/row-pagination/rowPaginationFeature.utils.ts @@ -32,6 +32,11 @@ export function getDefaultPaginationState(): PaginationState { * client-side pagination behavior allows it. Manual pagination opts out unless * the reset options explicitly opt back in. * + * When the reset is disabled for client-side pagination, an out-of-range + * page index is clamped to the last existing page instead, so shrinking the + * rows (removing data, filtering, grouping) never leaves the table on a page + * that no longer exists. An in-range page index is left untouched. + * * @example * ```ts * table_autoResetPageIndex(table) @@ -41,6 +46,9 @@ export function table_autoResetPageIndex< TFeatures extends TableFeatures, TData extends RowData, >(table: Table_Internal) { + const currentPageIndex = + table.atoms.pagination?.get()?.pageIndex ?? defaultPageIndex + if ( table.options.autoResetAll ?? table.options.autoResetPageIndex ?? @@ -50,11 +58,24 @@ export function table_autoResetPageIndex< // already on the default page. Routing a no-op through the pagination // handler would still run user `onPaginationChange` side effects (such // as refetching) on every data, filter, sort, or grouping change. - const currentPageIndex = - table.atoms.pagination?.get()?.pageIndex ?? defaultPageIndex if (currentPageIndex === defaultPageIndex) return table_resetPageIndex(table, true) + return } + + // Manual pagination owns its page range, so only client-side pagination + // is clamped. + if (table.options.manualPagination) return + + const pageCount = table_getPageCount(table) + // A negative or non-finite count means the page range is unknown. + if (!Number.isFinite(pageCount) || pageCount < 0) return + + const lastPageIndex = Math.max(0, pageCount - 1) + // Same no-op guard as above: only route through the handler when the + // index actually has to move. + if (currentPageIndex <= lastPageIndex) return + table_setPageIndex(table, lastPageIndex) } /** diff --git a/packages/table-core/tests/implementation/core/autoReset.test.ts b/packages/table-core/tests/implementation/core/autoReset.test.ts index 09ee50ebd2..08214413b9 100644 --- a/packages/table-core/tests/implementation/core/autoReset.test.ts +++ b/packages/table-core/tests/implementation/core/autoReset.test.ts @@ -9,6 +9,7 @@ import { createPaginatedRowModel, createSortedRowModel, filterFns, + functionalUpdate, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, @@ -19,6 +20,7 @@ import { testFeatures } from '../../fixtures/features' import type { ColumnDef, ExpandedState, + PaginationState, SortingState, TableOptions, } from '../../../src' @@ -221,9 +223,12 @@ describe('autoResetPageIndex end-to-end wiring', () => { }) describe('option precedence', () => { + // Page 1 stays valid after the `even` filter (3 rows / pageSize 2 = 2 + // pages), so "skipped" (stays 1) is distinguishable from "reset" (0) + // without landing on a page that no longer exists. async function triggerReset(table: ReturnType) { await primeTable(table) - table.setPageIndex(2) + table.setPageIndex(1) table.setColumnFilters([{ id: 'group', value: 'even' }]) table.getRowModel() await flushMicrotasks() @@ -232,7 +237,7 @@ describe('autoResetPageIndex end-to-end wiring', () => { it('should skip the reset when autoResetAll is false', async () => { const table = makeTable({ autoResetAll: false }) await triggerReset(table) - expect(table.atoms.pagination.get().pageIndex).toBe(2) + expect(table.atoms.pagination.get().pageIndex).toBe(1) }) it('should force the reset when autoResetAll is true even with manualPagination', async () => { @@ -244,7 +249,7 @@ describe('autoResetPageIndex end-to-end wiring', () => { it('should skip the reset when autoResetPageIndex is false', async () => { const table = makeTable({ autoResetPageIndex: false }) await triggerReset(table) - expect(table.atoms.pagination.get().pageIndex).toBe(2) + expect(table.atoms.pagination.get().pageIndex).toBe(1) }) it('should opt back in with autoResetPageIndex true despite manualPagination', async () => { @@ -259,11 +264,160 @@ describe('autoResetPageIndex end-to-end wiring', () => { it('should skip the reset by default when manualPagination is true', async () => { const table = makeTable({ manualPagination: true }) await triggerReset(table) - expect(table.atoms.pagination.get().pageIndex).toBe(2) + expect(table.atoms.pagination.get().pageIndex).toBe(1) }) }) }) +describe('pageIndex clamp when the page-index auto-reset is disabled (#4994)', () => { + // makeTable(): 6 rows, pageSize 2 -> 3 pages (0, 1, 2). + async function onLastPage(table: ReturnType) { + await primeTable(table) + table.setPageIndex(2) + expect(table.atoms.pagination.get().pageIndex).toBe(2) + } + + it('clamps to the last page when data shrinks', async () => { + const table = makeTable({ autoResetPageIndex: false }) + await onLastPage(table) + + // 3 rows -> 2 pages (0, 1) + table.setOptions((old) => ({ ...old, data: makeData().slice(0, 3) })) + table.getRowModel() + await flushMicrotasks() + + expect(table.atoms.pagination.get().pageIndex).toBe(1) + expect(table.getRowModel().rows.length).toBeGreaterThan(0) + }) + + it('clamps to the last page when a filter shrinks the rows', async () => { + const table = makeTable({ autoResetPageIndex: false }) + await onLastPage(table) + + // `even` keeps 3 rows -> 2 pages (0, 1) + table.setColumnFilters([{ id: 'group', value: 'even' }]) + table.getRowModel() + await flushMicrotasks() + + expect(table.atoms.pagination.get().pageIndex).toBe(1) + expect(table.getRowModel().rows.length).toBeGreaterThan(0) + }) + + it('clamps when autoResetAll is false', async () => { + const table = makeTable({ autoResetAll: false }) + await onLastPage(table) + + table.setColumnFilters([{ id: 'group', value: 'even' }]) + table.getRowModel() + await flushMicrotasks() + + expect(table.atoms.pagination.get().pageIndex).toBe(1) + }) + + it('clamps to page 0 when every row is removed', async () => { + const table = makeTable({ autoResetPageIndex: false }) + await onLastPage(table) + + table.setOptions((old) => ({ ...old, data: [] })) + table.getRowModel() + await flushMicrotasks() + + expect(table.atoms.pagination.get().pageIndex).toBe(0) + }) + + it('does not touch an in-range pageIndex', async () => { + const table = makeTable({ autoResetPageIndex: false }) + await primeTable(table) + table.setPageIndex(1) + + // `even` keeps 2 pages, so page 1 still exists + table.setColumnFilters([{ id: 'group', value: 'even' }]) + table.getRowModel() + await flushMicrotasks() + + expect(table.atoms.pagination.get().pageIndex).toBe(1) + }) + + it('does not call onPaginationChange for an in-range pageIndex', async () => { + const onPaginationChange = vi.fn() + const table = makeTable({ + autoResetPageIndex: false, + state: { pagination: { pageIndex: 2, pageSize: 2 } }, + onPaginationChange, + }) + await primeTable(table) + + // New data reference, same 6 rows -> still 3 pages, page 2 is valid + table.setOptions((old) => ({ ...old, data: makeData() })) + table.getRowModel() + await flushMicrotasks() + + expect(onPaginationChange).not.toHaveBeenCalled() + }) + + it('pushes the clamp through onPaginationChange for controlled state', async () => { + const onPaginationChange = vi.fn() + const table = makeTable({ + autoResetPageIndex: false, + state: { pagination: { pageIndex: 2, pageSize: 2 } }, + onPaginationChange, + }) + await primeTable(table) + + table.setOptions((old) => ({ ...old, data: makeData().slice(0, 3) })) + table.getRowModel() + await flushMicrotasks() + + // Each row-model stage that recomputes schedules the hook; with + // non-echoing controlled state every call carries the same clamp. + expect(onPaginationChange).toHaveBeenCalled() + for (const [updater] of onPaginationChange.mock.calls) { + expect( + functionalUpdate(updater, { + pageIndex: 2, + pageSize: 2, + }), + ).toEqual({ pageIndex: 1, pageSize: 2 }) + } + }) + + it('does not clamp with manualPagination (server owns the page range)', async () => { + const table = makeTable({ manualPagination: true }) + await onLastPage(table) + + table.setColumnFilters([{ id: 'group', value: 'even' }]) + table.getRowModel() + await flushMicrotasks() + + expect(table.atoms.pagination.get().pageIndex).toBe(2) + }) + + it('does not clamp with manualPagination and autoResetPageIndex false', async () => { + const table = makeTable({ + manualPagination: true, + autoResetPageIndex: false, + }) + await onLastPage(table) + + table.setColumnFilters([{ id: 'group', value: 'even' }]) + table.getRowModel() + await flushMicrotasks() + + expect(table.atoms.pagination.get().pageIndex).toBe(2) + }) + + it('does not clamp when the page count is unknown', async () => { + const table = makeTable({ autoResetPageIndex: false, pageCount: -1 }) + await onLastPage(table) + + table.setColumnFilters([{ id: 'group', value: 'even' }]) + table.getRowModel() + await flushMicrotasks() + + expect(table.atoms.pagination.get().pageIndex).toBe(2) + }) +}) + describe('autoResetSorting end-to-end wiring', () => { const ageSorting: SortingState = [{ id: 'age', desc: true }] const nameSorting: SortingState = [{ id: 'name', desc: false }] From 6daabb725c9f6d01da3b087389202c371b0898bc Mon Sep 17 00:00:00 2001 From: cipherprofessor Date: Wed, 23 Sep 2026 18:32:41 +0530 Subject: [PATCH 2/4] fix(table-core): skip page-count work on page 0 and narrow clamp docs wording --- .../clamp-page-index-when-reset-disabled.md | 2 +- docs/framework/alpine/guide/pagination.md | 2 +- docs/framework/angular/guide/pagination.md | 2 +- docs/framework/ember/guide/pagination.md | 2 +- docs/framework/lit/guide/pagination.md | 2 +- docs/framework/octane/guide/pagination.md | 2 +- docs/framework/preact/guide/pagination.md | 2 +- docs/framework/react/guide/pagination.md | 2 +- docs/framework/solid/guide/pagination.md | 2 +- docs/framework/svelte/guide/pagination.md | 2 +- docs/framework/vue/guide/pagination.md | 2 +- .../rowPaginationFeature.types.ts | 2 +- .../rowPaginationFeature.utils.ts | 25 ++++++++++---- .../implementation/core/autoReset.test.ts | 33 +++++++++++++++++++ 14 files changed, 63 insertions(+), 19 deletions(-) diff --git a/.changeset/clamp-page-index-when-reset-disabled.md b/.changeset/clamp-page-index-when-reset-disabled.md index 838696aa95..3f8cba68cb 100644 --- a/.changeset/clamp-page-index-when-reset-disabled.md +++ b/.changeset/clamp-page-index-when-reset-disabled.md @@ -2,4 +2,4 @@ '@tanstack/table-core': patch --- -Clamp an out-of-range `pageIndex` to the last page when rows shrink and `autoResetPageIndex` (or `autoResetAll`) is `false` with client-side pagination, instead of leaving the table on an empty page +Clamp an out-of-range `pageIndex` to the last page when a data, filter, or grouping change shrinks the rows and `autoResetPageIndex` (or `autoResetAll`) is `false` with client-side pagination, instead of leaving the table on an empty page diff --git a/docs/framework/alpine/guide/pagination.md b/docs/framework/alpine/guide/pagination.md index c4b8bb3e0a..31d36fd483 100644 --- a/docs/framework/alpine/guide/pagination.md +++ b/docs/framework/alpine/guide/pagination.md @@ -231,7 +231,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/angular/guide/pagination.md b/docs/framework/angular/guide/pagination.md index 91a4b76507..25f42270d5 100644 --- a/docs/framework/angular/guide/pagination.md +++ b/docs/framework/angular/guide/pagination.md @@ -321,7 +321,7 @@ readonly table = injectTable(() => ({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/ember/guide/pagination.md b/docs/framework/ember/guide/pagination.md index 1766da55ce..720e6f55d0 100644 --- a/docs/framework/ember/guide/pagination.md +++ b/docs/framework/ember/guide/pagination.md @@ -216,7 +216,7 @@ const table = useTable(() => ({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/lit/guide/pagination.md b/docs/framework/lit/guide/pagination.md index 9b1e6ee3f6..31440fe4f6 100644 --- a/docs/framework/lit/guide/pagination.md +++ b/docs/framework/lit/guide/pagination.md @@ -230,7 +230,7 @@ const table = this.tableController.table({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/octane/guide/pagination.md b/docs/framework/octane/guide/pagination.md index 9c5a8aec5e..a4fe119be6 100644 --- a/docs/framework/octane/guide/pagination.md +++ b/docs/framework/octane/guide/pagination.md @@ -216,7 +216,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/preact/guide/pagination.md b/docs/framework/preact/guide/pagination.md index dc275c5ec4..1dd34d24a2 100644 --- a/docs/framework/preact/guide/pagination.md +++ b/docs/framework/preact/guide/pagination.md @@ -302,7 +302,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/react/guide/pagination.md b/docs/framework/react/guide/pagination.md index e9eed2d702..cbfe0186ca 100644 --- a/docs/framework/react/guide/pagination.md +++ b/docs/framework/react/guide/pagination.md @@ -299,7 +299,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/solid/guide/pagination.md b/docs/framework/solid/guide/pagination.md index 432acd7654..7b6f92c02f 100644 --- a/docs/framework/solid/guide/pagination.md +++ b/docs/framework/solid/guide/pagination.md @@ -319,7 +319,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/svelte/guide/pagination.md b/docs/framework/svelte/guide/pagination.md index deae567002..ec32fabb15 100644 --- a/docs/framework/svelte/guide/pagination.md +++ b/docs/framework/svelte/guide/pagination.md @@ -328,7 +328,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/vue/guide/pagination.md b/docs/framework/vue/guide/pagination.md index 16ebcef252..fce210c164 100644 --- a/docs/framework/vue/guide/pagination.md +++ b/docs/framework/vue/guide/pagination.md @@ -329,7 +329,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after the rows shrink (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts b/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts index f063bdebe7..988a13ab54 100644 --- a/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts +++ b/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts @@ -19,7 +19,7 @@ export interface TableOptions_RowPagination { /** * If set to `true`, pagination will be reset to the first page when page-altering state changes e.g. `data` is updated, filters change, grouping changes, etc. * - * If set to `false` with client-side pagination, the current page is kept, but a page index that no longer exists after the rows shrink is clamped to the last page. + * If set to `false` with client-side pagination, the current page is kept, but a page index that no longer exists after a data, filter, or grouping change shrinks the rows is clamped to the last page. */ autoResetPageIndex?: boolean /** diff --git a/packages/table-core/src/features/row-pagination/rowPaginationFeature.utils.ts b/packages/table-core/src/features/row-pagination/rowPaginationFeature.utils.ts index 0f608582c6..9cd13506c6 100644 --- a/packages/table-core/src/features/row-pagination/rowPaginationFeature.utils.ts +++ b/packages/table-core/src/features/row-pagination/rowPaginationFeature.utils.ts @@ -33,9 +33,10 @@ export function getDefaultPaginationState(): PaginationState { * the reset options explicitly opt back in. * * When the reset is disabled for client-side pagination, an out-of-range - * page index is clamped to the last existing page instead, so shrinking the - * rows (removing data, filtering, grouping) never leaves the table on a page - * that no longer exists. An in-range page index is left untouched. + * page index is clamped to the last existing page instead, so a data, + * filter, or grouping change that shrinks the rows never leaves the table + * on a page that no longer exists. An in-range page index is left + * untouched. * * @example * ```ts @@ -63,17 +64,27 @@ export function table_autoResetPageIndex< return } + clampPageIndex(table, currentPageIndex) +} + +function clampPageIndex( + table: Table_Internal, + currentPageIndex: number, +) { // Manual pagination owns its page range, so only client-side pagination - // is clamped. - if (table.options.manualPagination) return + // is clamped. Page 0 always exists, so skip computing the page count + // (and the row-model stages behind it) there. + if (table.options.manualPagination || currentPageIndex <= defaultPageIndex) { + return + } const pageCount = table_getPageCount(table) // A negative or non-finite count means the page range is unknown. if (!Number.isFinite(pageCount) || pageCount < 0) return const lastPageIndex = Math.max(0, pageCount - 1) - // Same no-op guard as above: only route through the handler when the - // index actually has to move. + // Same no-op guard as the reset path: only route through the handler when + // the index actually has to move. if (currentPageIndex <= lastPageIndex) return table_setPageIndex(table, lastPageIndex) } diff --git a/packages/table-core/tests/implementation/core/autoReset.test.ts b/packages/table-core/tests/implementation/core/autoReset.test.ts index 08214413b9..506cf055de 100644 --- a/packages/table-core/tests/implementation/core/autoReset.test.ts +++ b/packages/table-core/tests/implementation/core/autoReset.test.ts @@ -416,6 +416,39 @@ describe('pageIndex clamp when the page-index auto-reset is disabled (#4994)', ( expect(table.atoms.pagination.get().pageIndex).toBe(2) }) + + it('does not compute downstream row models on page 0', async () => { + const filterSpy = vi.fn(() => true) + const table = constructTable({ + features, + columns: [ + { accessorKey: 'name', id: 'name' }, + { accessorKey: 'age', id: 'age' }, + { accessorKey: 'group', id: 'group', filterFn: filterSpy }, + ], + data: makeData(), + getSubRows: (row) => row.subRows, + initialState: { + pagination: { pageIndex: 0, pageSize: 2 }, + columnFilters: [{ id: 'group', value: 'even' }], + }, + autoResetPageIndex: false, + }) + + table.getCoreRowModel() + await flushMicrotasks() + await flushMicrotasks() + + filterSpy.mockClear() + + table.setOptions((old) => ({ ...old, data: makeData() })) + table.getCoreRowModel() + await flushMicrotasks() + await flushMicrotasks() + + expect(table.atoms.pagination.get().pageIndex).toBe(0) + expect(filterSpy).not.toHaveBeenCalled() + }) }) describe('autoResetSorting end-to-end wiring', () => { From 1ce93e7732378eadf2b7a1d539f36c4e1426fc9a Mon Sep 17 00:00:00 2001 From: cipherprofessor Date: Wed, 23 Sep 2026 19:24:42 +0530 Subject: [PATCH 3/4] docs: clarify that the row-count change triggers the pageIndex clamp --- docs/framework/alpine/guide/pagination.md | 2 +- docs/framework/angular/guide/pagination.md | 2 +- docs/framework/ember/guide/pagination.md | 2 +- docs/framework/lit/guide/pagination.md | 2 +- docs/framework/octane/guide/pagination.md | 2 +- docs/framework/preact/guide/pagination.md | 2 +- docs/framework/react/guide/pagination.md | 2 +- docs/framework/solid/guide/pagination.md | 2 +- docs/framework/svelte/guide/pagination.md | 2 +- docs/framework/vue/guide/pagination.md | 2 +- .../src/features/row-pagination/rowPaginationFeature.types.ts | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/framework/alpine/guide/pagination.md b/docs/framework/alpine/guide/pagination.md index 31d36fd483..887d4a678b 100644 --- a/docs/framework/alpine/guide/pagination.md +++ b/docs/framework/alpine/guide/pagination.md @@ -231,7 +231,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/angular/guide/pagination.md b/docs/framework/angular/guide/pagination.md index 25f42270d5..b450cfbbf3 100644 --- a/docs/framework/angular/guide/pagination.md +++ b/docs/framework/angular/guide/pagination.md @@ -321,7 +321,7 @@ readonly table = injectTable(() => ({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/ember/guide/pagination.md b/docs/framework/ember/guide/pagination.md index 720e6f55d0..9a3367b406 100644 --- a/docs/framework/ember/guide/pagination.md +++ b/docs/framework/ember/guide/pagination.md @@ -216,7 +216,7 @@ const table = useTable(() => ({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/lit/guide/pagination.md b/docs/framework/lit/guide/pagination.md index 31440fe4f6..01c322d07c 100644 --- a/docs/framework/lit/guide/pagination.md +++ b/docs/framework/lit/guide/pagination.md @@ -230,7 +230,7 @@ const table = this.tableController.table({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/octane/guide/pagination.md b/docs/framework/octane/guide/pagination.md index a4fe119be6..78b359e54e 100644 --- a/docs/framework/octane/guide/pagination.md +++ b/docs/framework/octane/guide/pagination.md @@ -216,7 +216,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/preact/guide/pagination.md b/docs/framework/preact/guide/pagination.md index 1dd34d24a2..3b46dd44f0 100644 --- a/docs/framework/preact/guide/pagination.md +++ b/docs/framework/preact/guide/pagination.md @@ -302,7 +302,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/react/guide/pagination.md b/docs/framework/react/guide/pagination.md index cbfe0186ca..46b52b6bec 100644 --- a/docs/framework/react/guide/pagination.md +++ b/docs/framework/react/guide/pagination.md @@ -299,7 +299,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/solid/guide/pagination.md b/docs/framework/solid/guide/pagination.md index 7b6f92c02f..872df783f4 100644 --- a/docs/framework/solid/guide/pagination.md +++ b/docs/framework/solid/guide/pagination.md @@ -319,7 +319,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/svelte/guide/pagination.md b/docs/framework/svelte/guide/pagination.md index ec32fabb15..b0e2341e1a 100644 --- a/docs/framework/svelte/guide/pagination.md +++ b/docs/framework/svelte/guide/pagination.md @@ -328,7 +328,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/vue/guide/pagination.md b/docs/framework/vue/guide/pagination.md index fce210c164..36c372f1e5 100644 --- a/docs/framework/vue/guide/pagination.md +++ b/docs/framework/vue/guide/pagination.md @@ -329,7 +329,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off, a `pageIndex` that no longer exists after a data, filter, or grouping change shrinks the rows (for example, deleting the last rows on the last page) is clamped to the last page, so the table does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts b/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts index 988a13ab54..3876cc6d5a 100644 --- a/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts +++ b/packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts @@ -19,7 +19,7 @@ export interface TableOptions_RowPagination { /** * If set to `true`, pagination will be reset to the first page when page-altering state changes e.g. `data` is updated, filters change, grouping changes, etc. * - * If set to `false` with client-side pagination, the current page is kept, but a page index that no longer exists after a data, filter, or grouping change shrinks the rows is clamped to the last page. + * If set to `false` with client-side pagination, the current page is kept, but when a data, filter, or grouping change reduces the row count, a now-out-of-range page index is clamped to the last page. */ autoResetPageIndex?: boolean /** From bb0258a48f2b43777708335e0043a1fbea89e18e Mon Sep 17 00:00:00 2001 From: cipherprofessor Date: Wed, 23 Sep 2026 20:39:38 +0530 Subject: [PATCH 4/4] docs: mention autoResetAll in the pageIndex clamp note --- docs/framework/alpine/guide/pagination.md | 2 +- docs/framework/angular/guide/pagination.md | 2 +- docs/framework/ember/guide/pagination.md | 2 +- docs/framework/lit/guide/pagination.md | 2 +- docs/framework/octane/guide/pagination.md | 2 +- docs/framework/preact/guide/pagination.md | 2 +- docs/framework/react/guide/pagination.md | 2 +- docs/framework/solid/guide/pagination.md | 2 +- docs/framework/svelte/guide/pagination.md | 2 +- docs/framework/vue/guide/pagination.md | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/framework/alpine/guide/pagination.md b/docs/framework/alpine/guide/pagination.md index 887d4a678b..a095f76796 100644 --- a/docs/framework/alpine/guide/pagination.md +++ b/docs/framework/alpine/guide/pagination.md @@ -231,7 +231,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/angular/guide/pagination.md b/docs/framework/angular/guide/pagination.md index b450cfbbf3..57ff4606a2 100644 --- a/docs/framework/angular/guide/pagination.md +++ b/docs/framework/angular/guide/pagination.md @@ -321,7 +321,7 @@ readonly table = injectTable(() => ({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/ember/guide/pagination.md b/docs/framework/ember/guide/pagination.md index 9a3367b406..93a05ef0b3 100644 --- a/docs/framework/ember/guide/pagination.md +++ b/docs/framework/ember/guide/pagination.md @@ -216,7 +216,7 @@ const table = useTable(() => ({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/lit/guide/pagination.md b/docs/framework/lit/guide/pagination.md index 01c322d07c..d06bcaf3cb 100644 --- a/docs/framework/lit/guide/pagination.md +++ b/docs/framework/lit/guide/pagination.md @@ -230,7 +230,7 @@ const table = this.tableController.table({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/octane/guide/pagination.md b/docs/framework/octane/guide/pagination.md index 78b359e54e..e9638f9fda 100644 --- a/docs/framework/octane/guide/pagination.md +++ b/docs/framework/octane/guide/pagination.md @@ -216,7 +216,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/preact/guide/pagination.md b/docs/framework/preact/guide/pagination.md index 3b46dd44f0..727be47647 100644 --- a/docs/framework/preact/guide/pagination.md +++ b/docs/framework/preact/guide/pagination.md @@ -302,7 +302,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/react/guide/pagination.md b/docs/framework/react/guide/pagination.md index 46b52b6bec..f12202ef5a 100644 --- a/docs/framework/react/guide/pagination.md +++ b/docs/framework/react/guide/pagination.md @@ -299,7 +299,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/solid/guide/pagination.md b/docs/framework/solid/guide/pagination.md index 872df783f4..d64ef50813 100644 --- a/docs/framework/solid/guide/pagination.md +++ b/docs/framework/solid/guide/pagination.md @@ -319,7 +319,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/svelte/guide/pagination.md b/docs/framework/svelte/guide/pagination.md index b0e2341e1a..c970a01cf6 100644 --- a/docs/framework/svelte/guide/pagination.md +++ b/docs/framework/svelte/guide/pagination.md @@ -328,7 +328,7 @@ const table = createTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs diff --git a/docs/framework/vue/guide/pagination.md b/docs/framework/vue/guide/pagination.md index 36c372f1e5..2542fc9eda 100644 --- a/docs/framework/vue/guide/pagination.md +++ b/docs/framework/vue/guide/pagination.md @@ -329,7 +329,7 @@ const table = useTable({ A common reason to set `autoResetPageIndex: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise snap the user back to the first page. Setting the option to a static `false` keeps the current page when the row model recomputes. If you also use the expanding feature, pair it with `autoResetExpanded: false` so expanded rows do not collapse on edits. -When `autoResetPageIndex` is off and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. +When `autoResetPageIndex` is off or `autoResetAll` is `false` and a data, filter, or grouping change reduces the row count, the table clamps any now-out-of-range `pageIndex` to the last page, so it does not show an empty page. With `manualPagination`, keeping `pageIndex` within the server's page range is still up to you. ### Pagination APIs