Skip to content

fix: correct row behavior and expand feature guides - #6509

Merged
KevinVandy merged 1 commit into
betafrom
agent/fix-row-behavior-and-feature-guides
Aug 4, 2026
Merged

fix: correct row behavior and expand feature guides#6509
KevinVandy merged 1 commit into
betafrom
agent/fix-row-behavior-and-feature-guides

Conversation

@KevinVandy

@KevinVandy KevinVandy commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

  • keep cell-selection keyboard movement and range extension within the currently rendered pagination page
  • restore the natural depth and parent relationships of every nested row after grouping is cleared
  • expand the React kitchen-sink example to exercise cell selection, cell spanning, grouped rendering, and the associated regression coverage
  • document the v9 feature plugin architecture, including why the required features option produces smaller application bundles
  • centralize client-side versus server-side processing guidance and clarify that applications provide their own backend processing and fetching layer
  • move aggregation concepts into complete framework-specific guides and add framework-specific FlexRender guides

Bug fixes

Cell-selection movement previously used the pre-pagination display-order model. Arrow-key movement could therefore select a row on another page that was not rendered. Movement now uses the final row model while preserving display indexes for merged-cell calculations.

Clearing grouping previously reset depth and parentId only on top-level rows. Nested rows retained the shifted relationships written during grouping. The reset now recursively restores the natural tree structure for every descendant.

Documentation

The documentation now introduces the v9 feature system as the first core guide, explains the bundle-size tradeoff behind explicit feature registration, and distinguishes features from optional client-side row models and function registries.

The new shared processing guide replaces repeated client-side versus server-side explanations while leaving concise links in each feature guide. Aggregation is now documented entirely within each framework guide, and every adapter has guidance for its own FlexRender and flexRender APIs.

Validation

  • pnpm test passed before PR creation and was not rerun
  • pnpm test:e2e passed before PR creation and was not rerun
  • pnpm test:docs passed with no broken links across 1,254 Markdown files
  • git diff --check passed

Summary by CodeRabbit

  • New Features

    • Added comprehensive guides for features, client-side versus server-side processing, aggregation, and FlexRender across supported frameworks.
    • Expanded the React kitchen-sink example with cell selection, keyboard navigation, cell spanning, and selection styling.
    • Added navigation links to the new framework and core guides.
  • Bug Fixes

    • Improved cell-selection navigation so it remains within the active page.
    • Corrected grouped-row relationships after ungrouping.
    • Prevented undefined values in grouped table displays.
  • Documentation

    • Clarified filtering, sorting, pagination, virtualization, and aggregation guidance.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR refreshes TanStack Table documentation across framework adapters, adds new Features, Client-Side vs Server-Side, and FlexRender guides, and updates aggregation guidance. It also fixes paginated cell-selection navigation, recursively restores grouped-row relationships, and expands the React kitchen-sink selection example.

Changes

Documentation and navigation

Layer / File(s) Summary
Core guides and navigation
docs/config.json, docs/guide/*
Navigation now includes Features and Client-Side vs Server-Side. New guides document feature registration, processing choices, manual options, row models, and server-side data flow.
Framework rendering and operation guides
docs/framework/*/guide/*
Framework guides add FlexRender documentation, expand aggregation contracts, and align filtering, sorting, pagination, virtualization, and custom-feature guidance.
Reference wording updates
docs/reference/*, packages/table-core/src/*types.ts
Documentation examples consistently use “e.g.” and clarify option descriptions.

Table interaction behavior

Layer / File(s) Summary
Core navigation and grouped-row relationships
packages/table-core/src/features/cell-selection/*, packages/table-core/src/features/column-grouping/*, packages/table-core/tests/implementation/features/*
Cell-selection navigation now stays within the rendered pagination model. Ungrouping recursively restores row depths and parent IDs. Tests cover both behaviors.
Kitchen-sink cell selection
examples/react/kitchen-sink/*
The example adds selectable and spanned cells, keyboard controls, targeted row subscriptions, selection styling, and end-to-end coverage.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • TanStack/table#6455: Updates the same cell-selection implementation, pagination tests, and kitchen-sink example.
  • TanStack/table#6462: Shares FlexRender behavior and aggregated or placeholder cell rendering documentation.
  • TanStack/table#6487: Modifies cell-selection behavior for merged cells and rendered-row navigation.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant KitchenSink
  participant CellSelection
  participant TableRows
  User->>KitchenSink: Select cells with mouse or keyboard
  KitchenSink->>CellSelection: Update the selected range
  CellSelection->>TableRows: Notify affected rows
  TableRows-->>KitchenSink: Render selection and span states
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two primary changes: row behavior fixes and expanded feature documentation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/fix-row-behavior-and-feature-guides

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Aug 4, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 4cca65a

Command Status Duration Result
nx affected --targets=test:eslint,test:sherif,t... ✅ Succeeded 8m 39s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 1m 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-08-04 02:11:53 UTC

@KevinVandy
KevinVandy marked this pull request as ready for review August 4, 2026 02:02
@pkg-pr-new

pkg-pr-new Bot commented Aug 4, 2026

Copy link
Copy Markdown
More templates

@tanstack/alpine-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/alpine-table@6509

@tanstack/angular-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/angular-table@6509

@tanstack/angular-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/angular-table-devtools@6509

@tanstack/ember-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/ember-table@6509

@tanstack/lit-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/lit-table@6509

@tanstack/match-sorter-utils

npm i https://pkg.pr.new/TanStack/table/@tanstack/match-sorter-utils@6509

@tanstack/octane-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/octane-table@6509

@tanstack/preact-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/preact-table@6509

@tanstack/preact-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/preact-table-devtools@6509

@tanstack/react-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/react-table@6509

@tanstack/react-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/react-table-devtools@6509

@tanstack/solid-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/solid-table@6509

@tanstack/solid-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/solid-table-devtools@6509

@tanstack/svelte-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/svelte-table@6509

@tanstack/table-core

npm i https://pkg.pr.new/TanStack/table/@tanstack/table-core@6509

@tanstack/table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/table-devtools@6509

@tanstack/vue-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/vue-table@6509

@tanstack/vue-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/vue-table-devtools@6509

commit: 4cca65a

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/table-core/src/features/cell-selection/cellSelectionFeature.utils.ts (1)

921-932: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Merge-bounds lookup mixes row-model indices with display indices, and the new tests do not cover that path. table_getCellSelectionMergeBounds produces bounds in display-index space, but the start-merge lookup in stepCoordinate now passes a getRowModel().rows index. The two indices diverge as soon as pagination or filtering removes rows above the current page, and the added tests use no cell spanning, so the divergence stays hidden.

  • packages/table-core/src/features/cell-selection/cellSelectionFeature.utils.ts#L921-L932: compute rows[rowIndex]!.getDisplayIndex() once and pass it to findMergeBoundsAt, matching the landing lookup at lines 994-999.
  • packages/table-core/tests/implementation/features/cell-selection/cellSelectionFeature.test.ts#L481-L514: add a test with cell spanning enabled that moves out of a merged cell while pageIndex is greater than 0.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/table-core/src/features/cell-selection/cellSelectionFeature.utils.ts`
around lines 921 - 932, The start-merge lookup in stepCoordinate uses row-model
indices instead of display indices. In
packages/table-core/src/features/cell-selection/cellSelectionFeature.utils.ts#L921-L932,
pass rows[rowIndex]!.getDisplayIndex() to findMergeBoundsAt while preserving the
existing direction handling; in
packages/table-core/tests/implementation/features/cell-selection/cellSelectionFeature.test.ts#L481-L514,
add coverage with cell spanning enabled that moves out of a merged cell on a
page where pageIndex is greater than 0.
🧹 Nitpick comments (4)
examples/react/kitchen-sink/src/routes/index.tsx (3)

1008-1008: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add an accessible name and role to the focusable grid container.

The container is now focusable and receives arrow-key shortcuts. Screen-reader users get no indication of the interaction model. Add role="grid" and aria-label so the keyboard behavior is discoverable.

♿ Proposed change
-        <div className="table-container" ref={gridRef} tabIndex={0}>
+        <div
+          className="table-container"
+          ref={gridRef}
+          tabIndex={0}
+          role="grid"
+          aria-label="Kitchen sink data grid with cell selection"
+        >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/react/kitchen-sink/src/routes/index.tsx` at line 1008, Add
role="grid" and a descriptive aria-label to the focusable table-container div
using gridRef, so screen readers identify its grid interaction and keyboard
navigation behavior.

509-547: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hoist the cell.getCanSelect() result into a local constant.

The code calls cell.getCanSelect() four times in one render pass. One local constant makes the gating explicit and avoids repeated calls.

♻️ Proposed refactor
-  const selectionClassNames = cell.getCanSelect()
+  const canSelect = cell.getCanSelect()
+  const selectionClassNames = canSelect
     ? (() => {
-      tabIndex={cell.getCanSelect() ? cell.getTabIndex() : undefined}
-      onMouseDown={
-        cell.getCanSelect() ? cell.getSelectionStartHandler() : undefined
-      }
-      onMouseEnter={
-        cell.getCanSelect() ? cell.getSelectionExtendHandler() : undefined
-      }
+      tabIndex={canSelect ? cell.getTabIndex() : undefined}
+      onMouseDown={canSelect ? cell.getSelectionStartHandler() : undefined}
+      onMouseEnter={canSelect ? cell.getSelectionExtendHandler() : undefined}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/react/kitchen-sink/src/routes/index.tsx` around lines 509 - 547, In
the cell render logic, assign cell.getCanSelect() to a local constant once and
reuse it for selectionClassNames, tabIndex, onMouseDown, and onMouseEnter.
Preserve the existing conditional behavior while eliminating the repeated method
calls.

1033-1043: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the repeated cell-selection row subscription.

This Subscribe + rowSelectionKey block is identical to the one in PinnedRow at lines 578-588. Extract one wrapper component and use it in both places.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/react/kitchen-sink/src/routes/index.tsx` around lines 1033 - 1043,
Extract the shared cell-selection subscription into a wrapper component that
encapsulates Subscribe and rowSelectionKey, then replace the duplicated blocks
in PinnedRow and the current row rendering with that component. Preserve the
existing source, selection bounds, display index, row id, and key behavior at
both call sites.
packages/table-core/tests/implementation/features/cell-selection/cellSelectionFeature.test.ts (1)

481-514: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for merged cells on a non-first page.

Both tests use a table without cell spanning, so display indices equal row-model indices. That combination hides the coordinate-space mismatch in the start-merge lookup in cellSelectionFeature.utils.ts. Add a test that enables cell spanning and navigates from inside a merged cell while pageIndex is greater than 0.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/table-core/tests/implementation/features/cell-selection/cellSelectionFeature.test.ts`
around lines 481 - 514, The cell-selection pagination tests lack coverage for
merged cells on a non-first page. Update the relevant tests around “keeps
movement within the current pagination page” or add a focused case that enables
cell spanning, sets pagination.pageIndex greater than 0, and navigates from
inside a merged cell; assert selection remains within the current page and uses
the correct merged-cell coordinates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/framework/react/guide/flex-render.md`:
- Line 42: Update the FlexRender example so the footer prop references a defined
footer header: either introduce a Header object sourced from
table.getFooterGroups() or rename the existing variable to footerHeader and use
it consistently in the FlexRender call.

In `@docs/framework/solid/guide/column-filtering.md`:
- Line 318: Update the filterFn.autoRemove documentation sentence in the column
filtering guide so the example uses correct punctuation and capitalization:
replace “e.g. Some” with “e.g., some,” while preserving the rest of the
explanation.

In `@docs/guide/client-side-vs-server-side.md`:
- Line 49: Update the client-side row-capacity statement in the documentation to
qualify the 15-million-row benchmark as dependent on browser, page complexity,
columns, and available memory rather than claiming it is supported “with ease”;
preserve the practical-use caveat. Also correct the Object Prototypes Refactor
link by removing the duplicate slash in its URL.

In `@examples/react/kitchen-sink/src/index.css`:
- Around line 296-305: Add an empty line before the box-shadow declaration
following the --cell-edge-* custom properties to satisfy Stylelint’s
declaration-empty-line-before rule; leave the shadow values unchanged.

In `@examples/react/kitchen-sink/tests/e2e/smoke.spec.ts`:
- Around line 71-73: Update the sorting assertion in the smoke test to locate a
status cell with a rowspan value greater than 1, rather than requiring exactly
rowspan="2". Keep the visibility assertion, but make it target any rendered
spanned cell so it remains valid for the randomly generated data.

---

Outside diff comments:
In
`@packages/table-core/src/features/cell-selection/cellSelectionFeature.utils.ts`:
- Around line 921-932: The start-merge lookup in stepCoordinate uses row-model
indices instead of display indices. In
packages/table-core/src/features/cell-selection/cellSelectionFeature.utils.ts#L921-L932,
pass rows[rowIndex]!.getDisplayIndex() to findMergeBoundsAt while preserving the
existing direction handling; in
packages/table-core/tests/implementation/features/cell-selection/cellSelectionFeature.test.ts#L481-L514,
add coverage with cell spanning enabled that moves out of a merged cell on a
page where pageIndex is greater than 0.

---

Nitpick comments:
In `@examples/react/kitchen-sink/src/routes/index.tsx`:
- Line 1008: Add role="grid" and a descriptive aria-label to the focusable
table-container div using gridRef, so screen readers identify its grid
interaction and keyboard navigation behavior.
- Around line 509-547: In the cell render logic, assign cell.getCanSelect() to a
local constant once and reuse it for selectionClassNames, tabIndex, onMouseDown,
and onMouseEnter. Preserve the existing conditional behavior while eliminating
the repeated method calls.
- Around line 1033-1043: Extract the shared cell-selection subscription into a
wrapper component that encapsulates Subscribe and rowSelectionKey, then replace
the duplicated blocks in PinnedRow and the current row rendering with that
component. Preserve the existing source, selection bounds, display index, row
id, and key behavior at both call sites.

In
`@packages/table-core/tests/implementation/features/cell-selection/cellSelectionFeature.test.ts`:
- Around line 481-514: The cell-selection pagination tests lack coverage for
merged cells on a non-first page. Update the relevant tests around “keeps
movement within the current pagination page” or add a focused case that enables
cell spanning, sets pagination.pageIndex greater than 0, and navigates from
inside a merged cell; assert selection remains within the current page and uses
the correct merged-cell coordinates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d4e09f8d-eaae-45de-909b-21d429e229fa

📥 Commits

Reviewing files that changed from the base of the PR and between 82c32e0 and 4cca65a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (95)
  • docs/config.json
  • docs/framework/alpine/guide/aggregation.md
  • docs/framework/alpine/guide/column-filtering.md
  • docs/framework/alpine/guide/custom-features.md
  • docs/framework/alpine/guide/flex-render.md
  • docs/framework/alpine/guide/global-filtering.md
  • docs/framework/alpine/guide/pagination.md
  • docs/framework/alpine/guide/sorting.md
  • docs/framework/angular/guide/aggregation.md
  • docs/framework/angular/guide/column-filtering.md
  • docs/framework/angular/guide/custom-features.md
  • docs/framework/angular/guide/flex-render.md
  • docs/framework/angular/guide/global-filtering.md
  • docs/framework/angular/guide/migrating.md
  • docs/framework/angular/guide/pagination.md
  • docs/framework/angular/guide/sorting.md
  • docs/framework/angular/quick-start.md
  • docs/framework/ember/guide/aggregation.md
  • docs/framework/ember/guide/column-filtering.md
  • docs/framework/ember/guide/custom-features.md
  • docs/framework/ember/guide/flex-render.md
  • docs/framework/ember/guide/global-filtering.md
  • docs/framework/ember/guide/pagination.md
  • docs/framework/ember/guide/sorting.md
  • docs/framework/lit/guide/aggregation.md
  • docs/framework/lit/guide/column-filtering.md
  • docs/framework/lit/guide/custom-features.md
  • docs/framework/lit/guide/flex-render.md
  • docs/framework/lit/guide/global-filtering.md
  • docs/framework/lit/guide/pagination.md
  • docs/framework/lit/guide/sorting.md
  • docs/framework/octane/guide/aggregation.md
  • docs/framework/octane/guide/column-filtering.md
  • docs/framework/octane/guide/custom-features.md
  • docs/framework/octane/guide/flex-render.md
  • docs/framework/octane/guide/global-filtering.md
  • docs/framework/octane/guide/pagination.md
  • docs/framework/octane/guide/sorting.md
  • docs/framework/preact/guide/aggregation.md
  • docs/framework/preact/guide/column-filtering.md
  • docs/framework/preact/guide/custom-features.md
  • docs/framework/preact/guide/flex-render.md
  • docs/framework/preact/guide/global-filtering.md
  • docs/framework/preact/guide/pagination.md
  • docs/framework/preact/guide/sorting.md
  • docs/framework/react/guide/aggregation.md
  • docs/framework/react/guide/column-filtering.md
  • docs/framework/react/guide/custom-features.md
  • docs/framework/react/guide/flex-render.md
  • docs/framework/react/guide/global-filtering.md
  • docs/framework/react/guide/pagination.md
  • docs/framework/react/guide/sorting.md
  • docs/framework/solid/guide/aggregation.md
  • docs/framework/solid/guide/column-filtering.md
  • docs/framework/solid/guide/custom-features.md
  • docs/framework/solid/guide/flex-render.md
  • docs/framework/solid/guide/global-filtering.md
  • docs/framework/solid/guide/pagination.md
  • docs/framework/solid/guide/sorting.md
  • docs/framework/svelte/guide/aggregation.md
  • docs/framework/svelte/guide/column-filtering.md
  • docs/framework/svelte/guide/custom-features.md
  • docs/framework/svelte/guide/flex-render.md
  • docs/framework/svelte/guide/global-filtering.md
  • docs/framework/svelte/guide/pagination.md
  • docs/framework/svelte/guide/sorting.md
  • docs/framework/vanilla/guide/aggregation.md
  • docs/framework/vanilla/guide/flex-render.md
  • docs/framework/vue/guide/aggregation.md
  • docs/framework/vue/guide/column-filtering.md
  • docs/framework/vue/guide/custom-features.md
  • docs/framework/vue/guide/flex-render.md
  • docs/framework/vue/guide/global-filtering.md
  • docs/framework/vue/guide/pagination.md
  • docs/framework/vue/guide/sorting.md
  • docs/guide/aggregation.md
  • docs/guide/client-side-vs-server-side.md
  • docs/guide/column-defs.md
  • docs/guide/data.md
  • docs/guide/features.md
  • docs/reference/index/interfaces/ColumnDef_RowSorting.md
  • docs/reference/index/interfaces/TableOptions_Core.md
  • docs/reference/index/interfaces/TableOptions_RowPagination.md
  • docs/reference/index/interfaces/TableOptions_Rows.md
  • examples/react/kitchen-sink/package.json
  • examples/react/kitchen-sink/src/index.css
  • examples/react/kitchen-sink/src/routes/index.tsx
  • examples/react/kitchen-sink/tests/e2e/smoke.spec.ts
  • packages/table-core/src/core/rows/coreRowsFeature.types.ts
  • packages/table-core/src/features/cell-selection/cellSelectionFeature.utils.ts
  • packages/table-core/src/features/column-grouping/createGroupedRowModel.ts
  • packages/table-core/src/features/row-pagination/rowPaginationFeature.types.ts
  • packages/table-core/src/features/row-sorting/rowSortingFeature.types.ts
  • packages/table-core/tests/implementation/features/cell-selection/cellSelectionFeature.test.ts
  • packages/table-core/tests/implementation/features/column-grouping/createGroupedRowModel.test.ts
💤 Files with no reviewable changes (1)
  • docs/guide/aggregation.md

```tsx
import { FlexRender } from '@tanstack/react-table'

const footerContent = <FlexRender footer={header} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Define the footer header used by this example.

header is not declared in this standalone snippet. Show a Header object from table.getFooterGroups() or rename the variable to footerHeader.

Proposed fix
-const footerContent = <FlexRender footer={header} />
+const footerHeader = table.getFooterGroups()[0].headers[0]
+const footerContent = <FlexRender footer={footerHeader} />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/framework/react/guide/flex-render.md` at line 42, Update the FlexRender
example so the footer prop references a defined footer header: either introduce
a Header object sourced from table.getFooterGroups() or rename the existing
variable to footerHeader and use it consistently in the FlexRender call.

- `filterFn.resolveDataValue` - This optional "hanging" method normalizes each row's value before it is compared against the filter value. It is honored by every filter function built with the `constructFilterFn` helper, which includes all built-in filter functions.

- `filterFn.autoRemove` - This optional "hanging" method on any given `filterFn` is passed a filter value and expected to return `true` if the filter value should be removed from the filter state. eg. Some boolean-style filters may want to remove the filter value from the table state if the filter value is set to `false`. When provided, this test is authoritative: values it keeps stay in filter state even when they are empty strings, which the default heuristic would otherwise remove. An `undefined` filter value always clears the filter regardless.
- `filterFn.autoRemove` - This optional "hanging" method on any given `filterFn` is passed a filter value and expected to return `true` if the filter value should be removed from the filter state. e.g. Some boolean-style filters may want to remove the filter value from the table state if the filter value is set to `false`. When provided, this test is authoritative: values it keeps stay in filter state even when they are empty strings, which the default heuristic would otherwise remove. An `undefined` filter value always clears the filter regardless.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the punctuation after e.g..

Line 318 continues the sentence after e.g., so use lowercase text and a comma: e.g., some boolean-style filters.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/framework/solid/guide/column-filtering.md` at line 318, Update the
filterFn.autoRemove documentation sentence in the column filtering guide so the
example uses correct punctuation and capitalization: replace “e.g. Some” with
“e.g., some,” while preserving the rest of the explanation.


You might be tempted to rule out client-side processing based on what you think is a large dataset, but tables with just a few thousand rows are often practical in the browser. TanStack Table examples stress-test much larger datasets. In fact, we stress-test all TanStack Table features with 1 million rows client-side and expect usable performance. Actual performance depends on the number of columns, the size and shape of each row, the work performed by accessors and feature functions, and the devices your users have. Test with representative data and target hardware.

Older versions of TanStack Table used to start running into memory issues at about 1 million rows, but thanks to our [Object Prototypes Refactor](https://tanstack.com//blog/tanstack-table-v9-memory-performance) we can now claim to support up to 15 million rows client-side with ease. Is loading 15 million rows practical for your use-case? Probably not! But we hope you know that TanStack Table should give you headroom many client-side rows.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
git ls-files | grep -F 'docs/guide/client-side-vs-server-side.md' || true

echo "== target excerpt =="
sed -n '35,60p' 'docs/guide/client-side-vs-server-side.md' 2>/dev/null || true

echo "== nearby mentions of row limits and memory/hardware caveats =="
rg -n "15 million|10–16|memory issues|hardware|caveat|TanStack Table" 'docs/guide/client-side-vs-server-side.md' 2>/dev/null || true

echo "== all instances of blog tanstack table v9 memory performance URL =="
rg -n "tanstack\.com//blog/tanstack-table-v9-memory-performance|tanstack\.com/blog/tanstack-table-v9-memory-performance" . 2>/dev/null || true

Repository: TanStack/table

Length of output: 7420


🌐 Web query:

tanstack blog tanstack-table-v9-memory-performance 15 million rows 4 GB benchmark

💡 Result:

TanStack Table V9 introduces significant memory optimizations compared to V8, enabling the library to handle much larger datasets in the browser [1][2]. According to official benchmarks, while TanStack Table V8 was typically limited to handling approximately 1 million to 1.5 million rows before hitting memory constraints (often around the 4GB mark), TanStack Table V9 can manage between 10 million and 16 million rows within that same 4GB memory limit [1][3]. This improvement is primarily driven by a refactor that moves table API methods (for rows, columns, headers, and cells) onto shared prototypes rather than recreating them for every instance [1][4][5]. This approach substantially reduces memory overhead for large tables, with some scenarios demonstrating up to a 90% reduction in retained heap usage compared to V8 [1][2]. The benchmarks were conducted using a custom testing suite that utilizes Playwright and the Chrome DevTools Protocol to measure retained heap size [1][6]. It is important to note that these figures are optimistic estimates; actual performance in a production environment will depend on the complexity of the web page, the number of features enabled, and other memory pressures [1]. As of July 2026, TanStack Table V9 is in beta, incorporating these optimizations as part of a broader architectural update focused on state management, tree-shakable features, and improved performance [7].

Citations:


Qualify the 15-million-row benchmark claim.

The benchmark supports a 10–16 million row result under a 4 GB limit, with actual browser performance depending on page complexity and columns. This line should not say “support up to 15 million … with ease,” and the URL contains a double slash.

Required link fix
-https://tanstack.com//blog/tanstack-table-v9-memory-performance
+https://tanstack.com/blog/tanstack-table-v9-memory-performance
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Older versions of TanStack Table used to start running into memory issues at about 1 million rows, but thanks to our [Object Prototypes Refactor](https://tanstack.com//blog/tanstack-table-v9-memory-performance) we can now claim to support up to 15 million rows client-side with ease. Is loading 15 million rows practical for your use-case? Probably not! But we hope you know that TanStack Table should give you headroom many client-side rows.
Older versions of TanStack Table used to start running into memory issues at about 1 million rows, but thanks to our [Object Prototypes Refactor](https://tanstack.com/blog/tanstack-table-v9-memory-performance) we can now claim to support up to 15 million rows client-side with ease. Is loading 15 million rows practical for your use-case? Probably not! But we hope you know that TanStack Table should give you headroom many client-side rows.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/guide/client-side-vs-server-side.md` at line 49, Update the client-side
row-capacity statement in the documentation to qualify the 15-million-row
benchmark as dependent on browser, page complexity, columns, and available
memory rather than claiming it is supported “with ease”; preserve the
practical-use caveat. Also correct the Object Prototypes Refactor link by
removing the duplicate slash in its URL.

Source: MCP tools

Comment on lines +296 to +305
user-select: none;
--cell-edge-top: 0 0 0 0 transparent;
--cell-edge-right: 0 0 0 0 transparent;
--cell-edge-bottom: 0 0 0 0 transparent;
--cell-edge-left: 0 0 0 0 transparent;
box-shadow:
inset var(--cell-edge-top),
inset var(--cell-edge-right),
inset var(--cell-edge-bottom),
inset var(--cell-edge-left);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the Stylelint declaration-empty-line-before error.

Stylelint reports an error for the box-shadow declaration. Add an empty line before it.

🎨 Proposed fix
   --cell-edge-left: 0 0 0 0 transparent;
+
   box-shadow:
     inset var(--cell-edge-top),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
user-select: none;
--cell-edge-top: 0 0 0 0 transparent;
--cell-edge-right: 0 0 0 0 transparent;
--cell-edge-bottom: 0 0 0 0 transparent;
--cell-edge-left: 0 0 0 0 transparent;
box-shadow:
inset var(--cell-edge-top),
inset var(--cell-edge-right),
inset var(--cell-edge-bottom),
inset var(--cell-edge-left);
user-select: none;
--cell-edge-top: 0 0 0 0 transparent;
--cell-edge-right: 0 0 0 0 transparent;
--cell-edge-bottom: 0 0 0 0 transparent;
--cell-edge-left: 0 0 0 0 transparent;
box-shadow:
inset var(--cell-edge-top),
inset var(--cell-edge-right),
inset var(--cell-edge-bottom),
inset var(--cell-edge-left);
🧰 Tools
🪛 Stylelint (17.14.1)

[error] 301-305: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/react/kitchen-sink/src/index.css` around lines 296 - 305, Add an
empty line before the box-shadow declaration following the --cell-edge-* custom
properties to satisfy Stylelint’s declaration-empty-line-before rule; leave the
shadow values unchanged.

Source: Linters/SAST tools

Comment on lines +71 to +73
const statusHeader = table.locator('th').filter({ hasText: 'Status' })
await statusHeader.locator('.sortable-header').click()
await expect(table.locator('tbody td[rowspan="2"]').first()).toBeVisible()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The exact rowspan="2" assertion can fail intermittently.

The route loads 1,000 randomly generated rows and the Status column has three possible values. After sorting by Status, adjacent equal values merge into runs. A run of exactly length 2 on the rendered page is not guaranteed; runs are usually longer. Assert that a spanned cell exists with a span greater than 1 instead.

🧪 Proposed fix
     const statusHeader = table.locator('th').filter({ hasText: 'Status' })
     await statusHeader.locator('.sortable-header').click()
-    await expect(table.locator('tbody td[rowspan="2"]').first()).toBeVisible()
+    const spannedCell = table
+      .locator('tbody td[rowspan]')
+      .filter({ has: page.locator(':scope:not([rowspan="1"])') })
+      .first()
+    await expect(spannedCell).toBeVisible()
+    expect(
+      Number(await spannedCell.getAttribute('rowspan')),
+    ).toBeGreaterThan(1)
     expect(errors).toEqual([])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/react/kitchen-sink/tests/e2e/smoke.spec.ts` around lines 71 - 73,
Update the sorting assertion in the smoke test to locate a status cell with a
rowspan value greater than 1, rather than requiring exactly rowspan="2". Keep
the visibility assertion, but make it target any rendered spanned cell so it
remains valid for the randomly generated data.

@KevinVandy
KevinVandy merged commit c6e8a34 into beta Aug 4, 2026
10 checks passed
@KevinVandy
KevinVandy deleted the agent/fix-row-behavior-and-feature-guides branch August 4, 2026 02:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant