Skip to content

fix: lots of various performance improvements#6268

Merged
KevinVandy merged 17 commits into
alphafrom
perf
May 16, 2026
Merged

fix: lots of various performance improvements#6268
KevinVandy merged 17 commits into
alphafrom
perf

Conversation

@KevinVandy
Copy link
Copy Markdown
Member

@KevinVandy KevinVandy commented May 16, 2026

🎯 Changes

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr.

Summary by CodeRabbit

  • New Features

    • Added “Kitchen Sink (All Features)” demos for Angular, Lit, Preact, React, Solid, Svelte, and Vue with full table UI, toolbars, pagination, stress-test controls, and live state dumps.
    • New demo UI components: debounced input, resizable cells, advanced column filter controls, indeterminate checkbox, and richer example toolbars.
  • Documentation

    • New React guide explaining subscribing to table state to avoid stale reads in nested components.
  • Performance

    • Repository-wide iteration/loop optimizations to reduce allocations and improve throughput.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb47a153-a8b5-45be-a709-ebe49e5a4aea

📥 Commits

Reviewing files that changed from the base of the PR and between e4f7a7c and 9241926.

📒 Files selected for processing (1)
  • examples/svelte/kitchen-sink/src/table-augmentations.ts

📝 Walkthrough

Walkthrough

Adds multi-framework “Kitchen Sink (All Features)” examples, updates docs/config.json and React guide, and applies table-core performance refactors (indexed loops, aggregation and memo/clone changes, visible-cells helper). Virtualized demos split row/column stress tests; ESLint and perf.md were added.

Changes

Feature examples and docs

Layer / File(s) Summary
Docs navigation and React table-state guide
docs/config.json, docs/framework/react/guide/table-state.md
Adds Kitchen Sink links for multiple frameworks and documents React Compiler-compatible state subscription and Subscribe usage.
Angular kitchen-sink app
examples/angular/kitchen-sink/...
Standalone Angular example with TanStack Table features, filtering UIs, resizing/pinning, data generation, styles, bootstrap, and project configs.
Lit kitchen-sink app and virtualized-columns updates
examples/lit/kitchen-sink/..., examples/lit/virtualized-columns/src/main.ts
Lit custom element example with full table UI, data gen, styles, Vite/tsconfig; virtualized example now supports separate row and column stress tests.
Preact kitchen-sink app
examples/preact/kitchen-sink/...
Preact example with fuzzy filtering, DnD headers/cells, pinning/resizing, styles, data generator, and Vite/TS configs.
React kitchen-sink app and virtualized-columns updates
examples/react/kitchen-sink/..., examples/react/virtualized-columns*/src/main.tsx
React example with DnD column reorder, pinning, resizing, filters, data gen, styles; virtualized demos manage columns/data in state and split stress tests.
Solid kitchen-sink app and virtualized-columns updates
examples/solid/kitchen-sink/..., examples/solid/virtualized-columns/src/App.tsx
Solid example with filtering/pinning/resizing, data gen, styles, bootstrap/configs; virtualized demo split stress tests for rows/columns.
Svelte kitchen-sink app and virtualized-columns updates
examples/svelte/kitchen-sink/..., examples/svelte/virtualized-columns/src/App.svelte
Svelte example with full table UI, filters, pinning/resizing, data gen, styles and configs; virtualized demo splits stress tests.
Vue kitchen-sink app and virtualized-columns updates
examples/vue/kitchen-sink/..., examples/vue/virtualized-columns/src/App.vue
Vue SFC example with full table demo, filter variants, pinning/resizing, data gen, styles, configs; virtualized demo uses reactive columns getter and separate stress tests.

Core table-core performance refactor

Layer / File(s) Summary
Utilities and aggregation functions
packages/table-core/src/utils.ts, packages/table-core/src/fns/aggregationFns.ts
Refactors clone/memo logic; removes noop/isNumberArray; rewrites median, unique, and uniqueCount aggregation implementations.
Core constructs: cells, columns, headers, rows, table
packages/table-core/src/core/**
Converts many iterator usages to indexed for loops; updates prototype assignment and header group/span computation and header flattening.
Column features: faceting, filtering, ordering, pinning, visibility
packages/table-core/src/features/column-*/**
Optimizes iteration patterns; refactors pinning/ordering helpers; adds row_getVisibleCellsByColumnId and exposes row API getVisibleCellsByColumnId.
Row pinning/selection/sorting and auto sort fn
packages/table-core/src/features/row-*/**
Refactors selection recursion to loop-based approach; avoids unnecessary cloning during row sorting; updates auto-sort sampling loops.
React-table FlexRender comment removal
packages/react-table/src/FlexRender.tsx
Removes an obsolete inline block comment.
ESLint rule and perf catalog doc
eslint.config.js, perf.md
Adds an override disabling @typescript-eslint/prefer-for-of in a temp config and adds perf.md documenting the refactor sweep and findings.

🎯 4 (Complex) | ⏱️ ~45 minutes

A rabbit taps keys with a grin,
Kitchen sinks bubble, features all in.
Columns stick, rows shuffle and sing,
Loops tightened, perf takes wing.
Hop onward — demos and code gleam bright! 🐇✨

✨ 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 perf

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented May 16, 2026

View your CI Pipeline Execution ↗ for commit fe98449

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

☁️ Nx Cloud last updated this comment at 2026-05-16 23:05:12 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 16, 2026

More templates

@tanstack/angular-table

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

@tanstack/lit-table

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

@tanstack/match-sorter-utils

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

@tanstack/preact-table

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

@tanstack/preact-table-devtools

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

@tanstack/react-table

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

@tanstack/react-table-devtools

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

@tanstack/solid-table

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

@tanstack/solid-table-devtools

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

@tanstack/svelte-table

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

@tanstack/table-core

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

@tanstack/table-devtools

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

@tanstack/vue-table

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

@tanstack/vue-table-devtools

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

commit: 9241926

nx-cloud[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (10)
perf.md-59-60 (1)

59-60: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Docs/config mismatch on rationale comment.

This section says the ESLint rule was flipped with a comment explaining the rationale, but eslint.config.js currently has no such comment near the override. Please either add the comment in config or update this sentence to avoid drift.

🤖 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 `@perf.md` around lines 59 - 60, The perf.md claim that the repo root
eslint.config.js was updated "with a comment explaining the rationale" is out of
sync with the actual config; either add an explanatory comment next to the
'`@typescript-eslint/prefer-for-of`' override in eslint.config.js (so the config
contains the rationale referenced) or edit perf.md to remove or reword that
sentence; locate the '`@typescript-eslint/prefer-for-of`' rule override in
eslint.config.js and either insert a short comment describing why it was flipped
to 'off' or change the perf.md sentence to accurately reflect that no comment
was added.
examples/vue/kitchen-sink/src/App.vue-2-2 (1)

2-2: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clear debounce timers on component unmount.

Pending timeouts can run after teardown and still invoke table setters; add cleanup to avoid stale async updates.

🧹 Suggested fix
-import { computed, ref } from 'vue'
+import { computed, onUnmounted, ref } from 'vue'
@@
 const debounceTimers = new Map<string, ReturnType<typeof setTimeout>>()
@@
 function debounceSet(key: string, setValue: () => void) {
@@
 }
+
+onUnmounted(() => {
+  for (const timer of debounceTimers.values()) {
+    clearTimeout(timer)
+  }
+  debounceTimers.clear()
+})

Also applies to: 209-220

🤖 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/vue/kitchen-sink/src/App.vue` at line 2, Import onUnmounted from
'vue' (alongside computed/ref), track any setTimeout IDs you create (e.g.,
debounceTimer or timerId used around the table update/debounce logic at
~209-220) by assigning them to a variable, and clear them in an onUnmounted hook
via clearTimeout(timerId); if you use lodash/underscore debounce, call
debouncedFn.cancel() in onUnmounted as well to prevent stale async calls to
table setter functions.
examples/vue/kitchen-sink/src/index.css-187-187 (1)

187-187: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix CSS keyword casing to satisfy linter.

The currentColor keyword should be lowercase currentcolor to comply with the value-keyword-case stylelint rule.

🎨 Proposed fix
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;

Apply this change to lines 187, 241, and 247.

Also applies to: 241-241, 247-247

🤖 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/vue/kitchen-sink/src/index.css` at line 187, Update the CSS keyword
casing for the border declarations that use the non-linted token: replace every
occurrence of "border: 1px solid currentColor;" with "border: 1px solid
currentcolor;" (i.e., change the keyword currentColor → currentcolor) for the
three occurrences referenced so the value-keyword-case rule is satisfied.
examples/angular/kitchen-sink/src/app/app.html-73-75 (1)

73-75: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Escape literal chevrons in button labels.

Use HTML entities for the pager/pin chevrons to avoid template/lint parser issues (e.g., Line [74], Line [84], Line [278], Line [285], Line [292], Line [299]).

🔧 Proposed fix
-                                <
+                                &lt;
...
-                                >
+                                &gt;
...
-      <<
+      &lt;&lt;
...
-      <
+      &lt;
...
-      >
+      &gt;
...
-      >>
+      &gt;&gt;

Also applies to: 83-85, 278-279, 285-286, 292-293, 299-300

🤖 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/angular/kitchen-sink/src/app/app.html` around lines 73 - 75, Button
labels currently contain literal chevrons (e.g. the < inside the <button
class="pin-button" (click)="header.column.pin('left')">) which can break
template parsing; update all button label contents that are raw '<' or '>'
(including other pin/pager buttons mentioned) to use HTML entities &lt; for '<'
and &gt; for '>' so the template parser and linters handle them safely, keeping
the surrounding attributes and click handlers (e.g., header.column.pin('left'))
unchanged; apply the same replacement to the other occurrences referenced in the
comment.
examples/angular/kitchen-sink/src/app/debounced-input/debounced-input.ts-11-20 (1)

11-20: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clear pending debounce timer during directive teardown.

A queued timeout can emit after the directive is destroyed; add cleanup to prevent stale post-destroy emissions.

🧹 Proposed fix
-import { Directive, HostListener, input, output } from '`@angular/core`'
+import { Directive, HostListener, OnDestroy, input, output } from '`@angular/core`'
@@
-export class DebouncedInput {
+export class DebouncedInput implements OnDestroy {
@@
   `@HostListener`('input', ['$event'])
   onInput(event: Event) {
     clearTimeout(this.timeout)
     this.timeout = setTimeout(() => {
       this.changeEvent.emit(event)
     }, this.debounce())
   }
+
+  ngOnDestroy() {
+    clearTimeout(this.timeout)
+  }
 }
🤖 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/angular/kitchen-sink/src/app/debounced-input/debounced-input.ts`
around lines 11 - 20, The directive's pending debounce timer (timeout property
used in onInput) can fire after the directive is destroyed; implement
ngOnDestroy on the class to clearTimeout(this.timeout) and set this.timeout =
undefined to prevent stale emissions, ensuring the teardown cancels any queued
setTimeout created by onInput's debounce logic.
examples/angular/kitchen-sink/src/styles.css-187-187 (1)

187-187: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix stylelint value-keyword-case violations (currentColorcurrentcolor).

These values currently fail lint checks.

Proposed fix
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;

Also applies to: 241-241, 247-247

🤖 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/angular/kitchen-sink/src/styles.css` at line 187, Replace the CSS
keyword value "currentColor" with lowercase "currentcolor" wherever used (e.g.,
the border property currently set as `border: 1px solid currentColor`) to
satisfy stylelint value-keyword-case; update the same keyword occurrences
reported (also at the other instances referenced) so all uses are lowercased.
examples/angular/kitchen-sink/src/app/table-filter/table-filter.ts-106-114 (1)

106-114: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify range filter value types for type safety.

The range filter handlers store string values from HTMLInputElement.value directly in the filter state typed as [number, number]. While the filtering works due to inNumberRange's built-in parseFloat conversion, the type mismatch creates unnecessary confusion. The type annotations should match the actual stored values.

Proposed fix
   readonly changeMinRangeValue = (event: Event) => {
-    const value = (event.target as HTMLInputElement).value
-    this.column().setFilterValue((old?: [number, number]) => [value, old?.[1]])
+    const raw = (event.target as HTMLInputElement).value
+    const value = raw === '' ? undefined : Number(raw)
+    this.column().setFilterValue(
+      (old?: [number | undefined, number | undefined]) => [value, old?.[1]]
+    )
   }

   readonly changeMaxRangeValue = (event: Event) => {
-    const value = (event.target as HTMLInputElement).value
-    this.column().setFilterValue((old?: [number, number]) => [old?.[0], value])
+    const raw = (event.target as HTMLInputElement).value
+    const value = raw === '' ? undefined : Number(raw)
+    this.column().setFilterValue(
+      (old?: [number | undefined, number | undefined]) => [old?.[0], value]
+    )
   }
🤖 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/angular/kitchen-sink/src/app/table-filter/table-filter.ts` around
lines 106 - 114, The handlers changeMinRangeValue and changeMaxRangeValue are
storing string input values into a filter typed as [number, number]; convert the
HTML input value to a numeric type before calling this.column().setFilterValue
to preserve type-safety: use parseFloat (or Number) on (event.target as
HTMLInputElement).value and pass the resulting number (or undefined/null if the
input is empty/invalid) into the [min, max] tuple in changeMinRangeValue and
changeMaxRangeValue so the stored filter values actually match the [number,
number] annotation.
examples/svelte/kitchen-sink/README.md-5-6 (1)

5-6: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix README run command to match actual scripts.

start is not defined in this example’s package.json, so these commands will fail for users.

Suggested patch
-- `npm run start` or `yarn start`
+- `npm run dev` or `yarn dev`
🤖 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/svelte/kitchen-sink/README.md` around lines 5 - 6, The README
currently instructs users to run `npm run start` / `yarn start`, but `start` is
not defined in this example's package.json; open package.json to confirm the
actual script name (commonly `dev` for Svelte examples) and update README.md
(the kitchen-sink README) to use the correct commands (e.g., change `npm run
start` / `yarn start` to `npm run dev` / `yarn dev` or whatever script name you
find), ensuring the install and run steps match the package.json "scripts"
entries.
examples/preact/kitchen-sink/src/index.css-187-187 (1)

187-187: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix stylelint keyword-case violations (currentColorcurrentcolor).

This currently fails value-keyword-case checks.

🎯 Suggested fix
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
...
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
...
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;

Also applies to: 241-241, 247-247

🤖 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/preact/kitchen-sink/src/index.css` at line 187, Replace the CSS
keyword casing for color keywords: change any occurrences of "currentColor" to
the lowercase "currentcolor" (e.g., in the rule containing "border: 1px solid
currentColor;" and the other instances noted around lines with the same
declaration) so the value-keyword-case stylelint rule passes; update each
"currentColor" token to "currentcolor" in the source file.
examples/react/kitchen-sink/src/index.css-187-187 (1)

187-187: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix stylelint keyword-case violations (currentcolor).

These values currently fail value-keyword-case and will keep lint red.

Minimal lint-fix diff
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
...
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
...
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;

Also applies to: 241-241, 247-247

🤖 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` at line 187, The CSS uses the
wrong casing for the color keyword — change any occurrences of the value
"currentColor" (e.g., in the border declarations such as the rule containing
"border: 1px solid currentColor;") to the lowercase canonical keyword
"currentcolor" to satisfy stylelint's value-keyword-case rule, and apply the
same fix to the other instances in the same file (the similar declarations noted
around the other occurrences).
🧹 Nitpick comments (6)
examples/vue/kitchen-sink/vite.config.ts (1)

5-7: ⚡ Quick win

Add CodeSandbox compatibility configuration.

Consider adding server: { allowedHosts: true } to enable DNS rebinding protection in CodeSandbox environments when using newer Vite versions. This configuration is forward-compatible and will be ignored by older Vite versions.

🔧 Proposed enhancement
 export default defineConfig({
   plugins: [vue(), vueJsx()],
+  server: {
+    allowedHosts: true,
+  },
 })

Based on learnings: In any Vite config file, add server: { allowedHosts: true } to enable DNS rebinding protection in CodeSandbox environments when using newer Vite versions (5.4.12+ or 6.0.9+).

🤖 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/vue/kitchen-sink/vite.config.ts` around lines 5 - 7, The Vite config
should include CodeSandbox-compatible DNS rebinding protection; inside the
exported defineConfig call (where plugins: [vue(), vueJsx()] is defined) add a
server property with allowedHosts: true (e.g., server: { allowedHosts: true })
so newer Vite versions accept CodeSandbox traffic while older versions ignore
it; update the object returned by defineConfig accordingly.
examples/vue/virtualized-columns/src/App.vue (1)

51-54: ⚡ Quick win

Consider using consistent pattern for reactive data.

The table configuration passes columns via a getter that returns columns.value, but data is passed as a ref directly. For clarity and consistency, consider using the same pattern for both.

♻️ Proposed consistency improvement

If both should use getters:

   get columns() {
     return columns.value
   },
-  data,
+  get data() {
+    return data.value
+  },

Or if both should be passed as refs:

-  get columns() {
-    return columns.value
-  },
+  columns,
   data,
🤖 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/vue/virtualized-columns/src/App.vue` around lines 51 - 54, The table
props are inconsistent: a getter named columns returns columns.value while data
is passed as a ref directly; make them consistent by choosing one pattern and
updating the other—either change the getter columns to expose the ref (remove
.value and return columns) so both props are refs, or change data to be a getter
(e.g., a data() { return data.value } or computed) so both props return plain
values; update the references to the symbols columns (getter) and data
accordingly throughout the component.
examples/lit/kitchen-sink/src/makeData.ts (1)

28-32: ⚡ Quick win

Use faker.helpers.arrayElement() instead of shuffle()[0] for status selection.

The current approach allocates and shuffles an array on every row generation, which is unnecessary overhead. faker.helpers.arrayElement() is the standard API for selecting a single random item from a fixed array and performs better.

♻️ Proposed refactor
+const STATUSES: Array<Person['status']> = [
+  'relationship',
+  'complicated',
+  'single',
+]
+
 const newPerson = (): Person => {
   return {
@@
-    status: faker.helpers.shuffle<Person['status']>([
-      'relationship',
-      'complicated',
-      'single',
-    ])[0],
+    status: faker.helpers.arrayElement(STATUSES),
   }
 }
🤖 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/lit/kitchen-sink/src/makeData.ts` around lines 28 - 32, Replace the
status selection that uses faker.helpers.shuffle(...)[0] with
faker.helpers.arrayElement([...]) to avoid allocating and shuffling the entire
array on each row; update the status assignment inside the Person generator (the
property using Person['status']) to call faker.helpers.arrayElement with the
same three status strings so it picks one item directly and more efficiently.
examples/preact/kitchen-sink/src/main.tsx (1)

567-567: ⚡ Quick win

Gate debug logging to development mode.

Always-on debugTable: true adds avoidable overhead/noise in stress datasets.

⚡ Suggested fix
-      debugTable: true,
+      debugTable: import.meta.env.DEV,
🤖 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/preact/kitchen-sink/src/main.tsx` at line 567, The config currently
sets debugTable: true unconditionally; change it to only enable in development
by making debugTable conditional (e.g., debugTable: import.meta.env?.DEV ||
process.env.NODE_ENV === 'development') so production builds default to false;
update the options object where debugTable is defined in main.tsx (the config
literal containing debugTable) to use that conditional expression.
packages/table-core/src/core/rows/constructRow.ts (1)

60-62: ⚡ Quick win

Cache the feature list outside constructRow.

Object.values(table._features) still allocates a fresh array per row. On large row models that's avoidable churn in the hottest path touched by this PR, so it would be better to reuse a precomputed feature list on the table and keep only the indexed loop here.

🤖 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/core/rows/constructRow.ts` around lines 60 - 62,
constructRow currently calls Object.values(table._features) on every row which
allocates a new array per call; cache a precomputed feature list on the table
(e.g., table._featureList or similar) when features are set/updated and update
that cache where features are mutated, then replace the per-call Object.values
usage inside constructRow with the indexed loop over table._featureList and call
initRowInstanceData on each item (features[i]!.initRowInstanceData?.(row));
ensure the cache is kept in sync with any code paths that add/remove features.
examples/react/kitchen-sink/src/main.tsx (1)

497-505: ⚡ Quick win

Move SortableContext outside per-cell loops to reduce render overhead.

Creating a new SortableContext for each cell is expensive and unnecessary; one context per row (or per section) is enough and scales better for 10k/100k stress modes.

Also applies to: 838-846

🤖 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/main.tsx` around lines 497 - 505, The current
code wraps each cell in its own SortableContext which is wasteful; move the
SortableContext so it wraps the entire row's cells instead of being created
inside row.getVisibleCells().map. Specifically, replace the per-cell
SortableContext usage by creating one SortableContext (using
items={table.store.state.columnOrder} and
strategy={horizontalListSortingStrategy}) around the mapped DragAlongCell
components (which use key={cell.id} and props cell and table), so DragAlongCell
remains unchanged but only a single SortableContext is rendered per row.
🤖 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 `@examples/angular/kitchen-sink/src/app/table-filter/table-filter.ts`:
- Around line 7-71: The `@Component` for TableFilter declares an imports array but
lacks standalone: true; update the TableFilter component decorator to include
standalone: true alongside imports so Angular will allow the imports property
(i.e., modify the `@Component`({...}) for the TableFilter component to add
standalone: true).

In `@examples/lit/kitchen-sink/vite.config.js`:
- Around line 8-10: The define block in vite.config.js is forcing
'process.env.NODE_ENV' to 'development' (and __DEV__ to true), which bakes dev
mode into builds; change the define logic to derive values from the current Vite
mode/command instead of hardcoding—use the incoming mode (or check mode ===
'production') to set 'process.env.NODE_ENV' to the actual mode string and
__DEV__ to a boolean based on mode (e.g., mode !== 'production') so production
builds get NODE_ENV='production' and dev-only branches can be tree-shaken.

In `@examples/preact/kitchen-sink/src/main.tsx`:
- Around line 316-325: Replace the non-interactive <span
className="sortable-header"> that uses column.getToggleSortingHandler() with a
semantic interactive control so keyboard users can sort; specifically, change it
to a button (or an element with role="button" and proper keyboard handling) that
calls column.getToggleSortingHandler() onClick and also responds to Enter/Space
via onKeyDown, preserve the table.FlexRender header rendering and the sort
indicator logic, and ensure any styling tied to .sortable-header is applied to
the new element so visual appearance remains unchanged.

In `@examples/react/kitchen-sink/index.html`:
- Line 7: The included third-party script tag loading
"https://unpkg.com/react-scan/dist/auto.global.js" lacks Subresource Integrity
(SRI) and crossorigin attributes; either remove the external script or add an
integrity attribute with the correct SHA384/sha512 SRI hash for that exact file
and set crossorigin="anonymous" on the same <script> element so browsers can
verify the resource. Locate the script tag referencing
"react-scan/dist/auto.global.js" and replace it by (a) removing the tag if the
example should not rely on external code, or (b) computing the SRI hash for that
exact build (e.g., using openssl or an SRI tool) and adding integrity="..." plus
crossorigin="anonymous" to the script element.

In `@examples/react/kitchen-sink/src/main.tsx`:
- Around line 223-227: The onChange handlers for DebouncedInput are storing
string values into the numeric range filter via column.setFilterValue; normalize
the incoming value before setting state by converting empty string '' to
undefined and numeric strings to Number(value) (so the stored tuple remains
[number|undefined, number|undefined]); apply this normalization in the onChange
that updates the first element (the shown diff) and the symmetric onChange that
updates the second element (the other occurrence around lines 237-241) so both
start and end bounds are always numbers or undefined.

In `@examples/react/kitchen-sink/vite.config.js`:
- Around line 9-14: The rollupReplace usage currently hardcodes development
replacements; change it to be mode-aware by computing the replacement values
based on Vite's mode (or process.env.NODE_ENV) instead of always setting __DEV__
to true and NODE_ENV to 'development'. Locate the rollupReplace call and replace
the static values object so that __DEV__ is true only when mode ===
'development' (or mode !== 'production') and 'process.env.NODE_ENV' is set to
JSON.stringify(mode || process.env.NODE_ENV || 'production') (production for
build), ensuring production builds get appropriate 'production' values to
preserve tree-shaking and optimizations.

In `@examples/solid/kitchen-sink/src/index.css`:
- Line 187: Change the CSS keyword casing for the color keyword used in the
border declarations: replace the occurrences of "currentColor" (e.g., in the
rule that sets "border: 1px solid currentColor;") with the lowercase
"currentcolor" so stylelint's keyword-case rule passes for the instances
referenced (the border declarations at the noted locations).

In `@examples/solid/kitchen-sink/vite.config.ts`:
- Around line 6-9: Remove the invalid Vite option build.polyfillDynamicImport
from the vite config: locate the build block in vite.config.ts (the object
containing target: 'esnext') and delete the polyfillDynamicImport property; if
legacy browser support is required, add and configure `@vitejs/plugin-legacy`
instead of reintroducing this removed option.

In `@examples/solid/virtualized-columns/src/App.tsx`:
- Around line 45-53: The stress test handlers reuse the current opposite
dimension causing combinatorial blow-up; update stressTestRows and
stressTestColumns so each pins the non-stressed dimension to a fixed default
instead of reading the live state. Specifically, introduce constants (e.g.,
DEFAULT_ROW_COUNT and DEFAULT_COLUMN_COUNT) set from the initial dataset/columns
and change stressTestRows to call setData(makeData(STRESS_ROW_COUNT,
makeColumns(DEFAULT_COLUMN_COUNT) or use the initial columns array) and change
stressTestColumns to setColumns(makeColumns(STRESS_COLUMN_COUNT)) and
setData(makeData(DEFAULT_ROW_COUNT, makeColumns(STRESS_COLUMN_COUNT))). Use the
existing functions/variables makeData, makeColumns, setData, setColumns,
stressTestRows, and stressTestColumns to locate and apply the fix.

In `@examples/svelte/kitchen-sink/vite.config.js`:
- Around line 6-17: The config currently hardcodes __DEV__ and
'process.env.NODE_ENV' to development which breaks production builds; change the
export to accept Vite's mode (export default defineConfig(({ mode }) => ({ ...
})) ) and set rollupReplace values dynamically, e.g., set __DEV__ to
JSON.stringify(mode !== 'production') and 'process.env.NODE_ENV' to
JSON.stringify(mode) (or use process.env.NODE_ENV || mode) inside the
rollupReplace plugin so production builds get correct production flags; update
references in the file to use defineConfig, rollupReplace, and the mode
variable.

In `@examples/vue/kitchen-sink/src/App.vue`:
- Around line 469-483: Replace the non-interactive <span> used for sorting with
a semantic <button> so keyboard users can focus and activate sorting;
specifically in the block using header.column.getCanSort() and
header.column.getToggleSortingHandler() (the element with class
"sortable-header" that renders <FlexRender :header="header" /> and the sort
indicator), change it to a <button type="button">, keep the
`@click`="header.column.getToggleSortingHandler()?.($event)" binding and the class
"sortable-header", and add an accessible state via :aria-sort that maps
header.column.getIsSorted() to "ascending"/"descending"/"none" so assistive tech
receives the sort state.

---

Minor comments:
In `@examples/angular/kitchen-sink/src/app/app.html`:
- Around line 73-75: Button labels currently contain literal chevrons (e.g. the
< inside the <button class="pin-button" (click)="header.column.pin('left')">)
which can break template parsing; update all button label contents that are raw
'<' or '>' (including other pin/pager buttons mentioned) to use HTML entities
&lt; for '<' and &gt; for '>' so the template parser and linters handle them
safely, keeping the surrounding attributes and click handlers (e.g.,
header.column.pin('left')) unchanged; apply the same replacement to the other
occurrences referenced in the comment.

In `@examples/angular/kitchen-sink/src/app/debounced-input/debounced-input.ts`:
- Around line 11-20: The directive's pending debounce timer (timeout property
used in onInput) can fire after the directive is destroyed; implement
ngOnDestroy on the class to clearTimeout(this.timeout) and set this.timeout =
undefined to prevent stale emissions, ensuring the teardown cancels any queued
setTimeout created by onInput's debounce logic.

In `@examples/angular/kitchen-sink/src/app/table-filter/table-filter.ts`:
- Around line 106-114: The handlers changeMinRangeValue and changeMaxRangeValue
are storing string input values into a filter typed as [number, number]; convert
the HTML input value to a numeric type before calling
this.column().setFilterValue to preserve type-safety: use parseFloat (or Number)
on (event.target as HTMLInputElement).value and pass the resulting number (or
undefined/null if the input is empty/invalid) into the [min, max] tuple in
changeMinRangeValue and changeMaxRangeValue so the stored filter values actually
match the [number, number] annotation.

In `@examples/angular/kitchen-sink/src/styles.css`:
- Line 187: Replace the CSS keyword value "currentColor" with lowercase
"currentcolor" wherever used (e.g., the border property currently set as
`border: 1px solid currentColor`) to satisfy stylelint value-keyword-case;
update the same keyword occurrences reported (also at the other instances
referenced) so all uses are lowercased.

In `@examples/preact/kitchen-sink/src/index.css`:
- Line 187: Replace the CSS keyword casing for color keywords: change any
occurrences of "currentColor" to the lowercase "currentcolor" (e.g., in the rule
containing "border: 1px solid currentColor;" and the other instances noted
around lines with the same declaration) so the value-keyword-case stylelint rule
passes; update each "currentColor" token to "currentcolor" in the source file.

In `@examples/react/kitchen-sink/src/index.css`:
- Line 187: The CSS uses the wrong casing for the color keyword — change any
occurrences of the value "currentColor" (e.g., in the border declarations such
as the rule containing "border: 1px solid currentColor;") to the lowercase
canonical keyword "currentcolor" to satisfy stylelint's value-keyword-case rule,
and apply the same fix to the other instances in the same file (the similar
declarations noted around the other occurrences).

In `@examples/svelte/kitchen-sink/README.md`:
- Around line 5-6: The README currently instructs users to run `npm run start` /
`yarn start`, but `start` is not defined in this example's package.json; open
package.json to confirm the actual script name (commonly `dev` for Svelte
examples) and update README.md (the kitchen-sink README) to use the correct
commands (e.g., change `npm run start` / `yarn start` to `npm run dev` / `yarn
dev` or whatever script name you find), ensuring the install and run steps match
the package.json "scripts" entries.

In `@examples/vue/kitchen-sink/src/App.vue`:
- Line 2: Import onUnmounted from 'vue' (alongside computed/ref), track any
setTimeout IDs you create (e.g., debounceTimer or timerId used around the table
update/debounce logic at ~209-220) by assigning them to a variable, and clear
them in an onUnmounted hook via clearTimeout(timerId); if you use
lodash/underscore debounce, call debouncedFn.cancel() in onUnmounted as well to
prevent stale async calls to table setter functions.

In `@examples/vue/kitchen-sink/src/index.css`:
- Line 187: Update the CSS keyword casing for the border declarations that use
the non-linted token: replace every occurrence of "border: 1px solid
currentColor;" with "border: 1px solid currentcolor;" (i.e., change the keyword
currentColor → currentcolor) for the three occurrences referenced so the
value-keyword-case rule is satisfied.

In `@perf.md`:
- Around line 59-60: The perf.md claim that the repo root eslint.config.js was
updated "with a comment explaining the rationale" is out of sync with the actual
config; either add an explanatory comment next to the
'`@typescript-eslint/prefer-for-of`' override in eslint.config.js (so the config
contains the rationale referenced) or edit perf.md to remove or reword that
sentence; locate the '`@typescript-eslint/prefer-for-of`' rule override in
eslint.config.js and either insert a short comment describing why it was flipped
to 'off' or change the perf.md sentence to accurately reflect that no comment
was added.

---

Nitpick comments:
In `@examples/lit/kitchen-sink/src/makeData.ts`:
- Around line 28-32: Replace the status selection that uses
faker.helpers.shuffle(...)[0] with faker.helpers.arrayElement([...]) to avoid
allocating and shuffling the entire array on each row; update the status
assignment inside the Person generator (the property using Person['status']) to
call faker.helpers.arrayElement with the same three status strings so it picks
one item directly and more efficiently.

In `@examples/preact/kitchen-sink/src/main.tsx`:
- Line 567: The config currently sets debugTable: true unconditionally; change
it to only enable in development by making debugTable conditional (e.g.,
debugTable: import.meta.env?.DEV || process.env.NODE_ENV === 'development') so
production builds default to false; update the options object where debugTable
is defined in main.tsx (the config literal containing debugTable) to use that
conditional expression.

In `@examples/react/kitchen-sink/src/main.tsx`:
- Around line 497-505: The current code wraps each cell in its own
SortableContext which is wasteful; move the SortableContext so it wraps the
entire row's cells instead of being created inside row.getVisibleCells().map.
Specifically, replace the per-cell SortableContext usage by creating one
SortableContext (using items={table.store.state.columnOrder} and
strategy={horizontalListSortingStrategy}) around the mapped DragAlongCell
components (which use key={cell.id} and props cell and table), so DragAlongCell
remains unchanged but only a single SortableContext is rendered per row.

In `@examples/vue/kitchen-sink/vite.config.ts`:
- Around line 5-7: The Vite config should include CodeSandbox-compatible DNS
rebinding protection; inside the exported defineConfig call (where plugins:
[vue(), vueJsx()] is defined) add a server property with allowedHosts: true
(e.g., server: { allowedHosts: true }) so newer Vite versions accept CodeSandbox
traffic while older versions ignore it; update the object returned by
defineConfig accordingly.

In `@examples/vue/virtualized-columns/src/App.vue`:
- Around line 51-54: The table props are inconsistent: a getter named columns
returns columns.value while data is passed as a ref directly; make them
consistent by choosing one pattern and updating the other—either change the
getter columns to expose the ref (remove .value and return columns) so both
props are refs, or change data to be a getter (e.g., a data() { return
data.value } or computed) so both props return plain values; update the
references to the symbols columns (getter) and data accordingly throughout the
component.

In `@packages/table-core/src/core/rows/constructRow.ts`:
- Around line 60-62: constructRow currently calls Object.values(table._features)
on every row which allocates a new array per call; cache a precomputed feature
list on the table (e.g., table._featureList or similar) when features are
set/updated and update that cache where features are mutated, then replace the
per-call Object.values usage inside constructRow with the indexed loop over
table._featureList and call initRowInstanceData on each item
(features[i]!.initRowInstanceData?.(row)); ensure the cache is kept in sync with
any code paths that add/remove features.
🪄 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

Run ID: e583b753-1dba-4ecb-a514-580b234a2913

📥 Commits

Reviewing files that changed from the base of the PR and between 05f3ed1 and e9e2b8a.

⛔ Files ignored due to path filters (3)
  • examples/angular/kitchen-sink/public/favicon.ico is excluded by !**/*.ico
  • examples/vue/kitchen-sink/public/favicon.ico is excluded by !**/*.ico
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (109)
  • docs/config.json
  • docs/framework/react/guide/table-state.md
  • eslint.config.js
  • examples/angular/kitchen-sink/.gitignore
  • examples/angular/kitchen-sink/README.md
  • examples/angular/kitchen-sink/angular.json
  • examples/angular/kitchen-sink/package.json
  • examples/angular/kitchen-sink/src/app/app.config.ts
  • examples/angular/kitchen-sink/src/app/app.html
  • examples/angular/kitchen-sink/src/app/app.ts
  • examples/angular/kitchen-sink/src/app/debounced-input/debounced-input.ts
  • examples/angular/kitchen-sink/src/app/makeData.ts
  • examples/angular/kitchen-sink/src/app/resizable-cell/resizable-cell.ts
  • examples/angular/kitchen-sink/src/app/table-filter/table-filter.ts
  • examples/angular/kitchen-sink/src/index.html
  • examples/angular/kitchen-sink/src/main.ts
  • examples/angular/kitchen-sink/src/styles.css
  • examples/angular/kitchen-sink/tsconfig.app.json
  • examples/angular/kitchen-sink/tsconfig.json
  • examples/lit/kitchen-sink/index.html
  • examples/lit/kitchen-sink/package.json
  • examples/lit/kitchen-sink/src/main.ts
  • examples/lit/kitchen-sink/src/makeData.ts
  • examples/lit/kitchen-sink/tsconfig.json
  • examples/lit/kitchen-sink/vite.config.js
  • examples/lit/virtualized-columns/src/main.ts
  • examples/preact/kitchen-sink/index.html
  • examples/preact/kitchen-sink/package.json
  • examples/preact/kitchen-sink/src/index.css
  • examples/preact/kitchen-sink/src/main.tsx
  • examples/preact/kitchen-sink/src/makeData.ts
  • examples/preact/kitchen-sink/src/vite-env.d.ts
  • examples/preact/kitchen-sink/tsconfig.json
  • examples/preact/kitchen-sink/vite.config.ts
  • examples/react/kitchen-sink/index.html
  • examples/react/kitchen-sink/package.json
  • examples/react/kitchen-sink/src/index.css
  • examples/react/kitchen-sink/src/main.tsx
  • examples/react/kitchen-sink/src/makeData.ts
  • examples/react/kitchen-sink/src/vite-env.d.ts
  • examples/react/kitchen-sink/tsconfig.json
  • examples/react/kitchen-sink/vite.config.js
  • examples/react/virtualized-columns-experimental/src/main.tsx
  • examples/react/virtualized-columns/src/main.tsx
  • examples/solid/kitchen-sink/.gitignore
  • examples/solid/kitchen-sink/README.md
  • examples/solid/kitchen-sink/index.html
  • examples/solid/kitchen-sink/package.json
  • examples/solid/kitchen-sink/src/App.tsx
  • examples/solid/kitchen-sink/src/index.css
  • examples/solid/kitchen-sink/src/index.tsx
  • examples/solid/kitchen-sink/src/makeData.ts
  • examples/solid/kitchen-sink/tsconfig.json
  • examples/solid/kitchen-sink/vite.config.ts
  • examples/solid/virtualized-columns/src/App.tsx
  • examples/svelte/kitchen-sink/.gitignore
  • examples/svelte/kitchen-sink/README.md
  • examples/svelte/kitchen-sink/index.html
  • examples/svelte/kitchen-sink/package.json
  • examples/svelte/kitchen-sink/src/App.svelte
  • examples/svelte/kitchen-sink/src/index.css
  • examples/svelte/kitchen-sink/src/main.ts
  • examples/svelte/kitchen-sink/src/makeData.ts
  • examples/svelte/kitchen-sink/svelte.config.js
  • examples/svelte/kitchen-sink/tsconfig.json
  • examples/svelte/kitchen-sink/vite.config.js
  • examples/svelte/virtualized-columns/src/App.svelte
  • examples/vue/kitchen-sink/.gitignore
  • examples/vue/kitchen-sink/.vscode/extensions.json
  • examples/vue/kitchen-sink/README.md
  • examples/vue/kitchen-sink/env.d.ts
  • examples/vue/kitchen-sink/index.html
  • examples/vue/kitchen-sink/package.json
  • examples/vue/kitchen-sink/src/App.vue
  • examples/vue/kitchen-sink/src/env.d.ts
  • examples/vue/kitchen-sink/src/index.css
  • examples/vue/kitchen-sink/src/main.ts
  • examples/vue/kitchen-sink/src/makeData.ts
  • examples/vue/kitchen-sink/tsconfig.json
  • examples/vue/kitchen-sink/vite.config.ts
  • examples/vue/virtualized-columns/src/App.vue
  • packages/react-table/src/FlexRender.tsx
  • packages/table-core/src/core/cells/constructCell.ts
  • packages/table-core/src/core/columns/constructColumn.ts
  • packages/table-core/src/core/columns/coreColumnsFeature.utils.ts
  • packages/table-core/src/core/headers/buildHeaderGroups.ts
  • packages/table-core/src/core/headers/constructHeader.ts
  • packages/table-core/src/core/headers/coreHeadersFeature.ts
  • packages/table-core/src/core/headers/coreHeadersFeature.utils.ts
  • packages/table-core/src/core/rows/constructRow.ts
  • packages/table-core/src/core/rows/coreRowsFeature.utils.ts
  • packages/table-core/src/core/table/constructTable.ts
  • packages/table-core/src/core/table/coreTablesFeature.utils.ts
  • packages/table-core/src/features/column-faceting/createFacetedMinMaxValues.ts
  • packages/table-core/src/features/column-faceting/createFacetedRowModel.ts
  • packages/table-core/src/features/column-faceting/createFacetedUniqueValues.ts
  • packages/table-core/src/features/column-filtering/createFilteredRowModel.ts
  • packages/table-core/src/features/column-ordering/columnOrderingFeature.utils.ts
  • packages/table-core/src/features/column-pinning/columnPinningFeature.utils.ts
  • packages/table-core/src/features/column-visibility/columnVisibilityFeature.ts
  • packages/table-core/src/features/column-visibility/columnVisibilityFeature.types.ts
  • packages/table-core/src/features/column-visibility/columnVisibilityFeature.utils.ts
  • packages/table-core/src/features/row-pinning/rowPinningFeature.utils.ts
  • packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
  • packages/table-core/src/features/row-sorting/createSortedRowModel.ts
  • packages/table-core/src/features/row-sorting/rowSortingFeature.utils.ts
  • packages/table-core/src/fns/aggregationFns.ts
  • packages/table-core/src/utils.ts
  • perf.md
💤 Files with no reviewable changes (1)
  • packages/react-table/src/FlexRender.tsx

Comment on lines +7 to +71
@Component({
selector: 'app-table-filter',
template: `
@switch (filterVariant()) {
@case ('range') {
<div class="filter-row">
<input
debouncedInput
[debounce]="300"
type="number"
class="filter-input"
[min]="column().getFacetedMinMaxValues()?.[0] ?? ''"
[max]="column().getFacetedMinMaxValues()?.[1] ?? ''"
[value]="columnFilterValue()?.[0] ?? ''"
[attr.placeholder]="minRangePlaceholder()"
(changeEvent)="changeMinRangeValue($event)"
/>
<input
debouncedInput
[debounce]="300"
type="number"
class="filter-input"
[min]="column().getFacetedMinMaxValues()?.[0] ?? ''"
[max]="column().getFacetedMinMaxValues()?.[1] ?? ''"
[value]="columnFilterValue()?.[1] ?? ''"
[attr.placeholder]="maxRangePlaceholder()"
(changeEvent)="changeMaxRangeValue($event)"
/>
</div>
}
@case ('select') {
<select
class="filter-select"
[value]="columnFilterValue()?.toString() ?? ''"
(change)="column().setFilterValue($any($event).target.value)"
>
<option value="">All</option>
@for (value of sortedUniqueValues(); track value) {
<option [value]="value">{{ value }}</option>
}
</select>
}
@default {
<datalist [id]="column().id + 'list'">
@for (value of sortedUniqueValues(); track value) {
<option [value]="value"></option>
}
</datalist>
<input
type="text"
class="filter-select"
debouncedInput
[debounce]="300"
[attr.placeholder]="
'Search (' + column().getFacetedUniqueValues().size + ')'
"
[attr.list]="column().id + 'list'"
[value]="columnFilterValue() ?? ''"
(changeEvent)="column().setFilterValue($any($event).target.value)"
/>
}
}
`,
imports: [DebouncedInput],
})
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.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify TableFilter metadata and where it is imported/used.
rg -n -C3 "`@Component`\\(|standalone|imports:\\s*\\[" examples/angular/kitchen-sink/src/app/table-filter/table-filter.ts
rg -n -C3 "\\bTableFilter\\b|imports:\\s*\\[" examples/angular/kitchen-sink/src/app/app.ts

Repository: TanStack/table

Length of output: 1105


🏁 Script executed:

rg -n "TableFilter" examples/angular/kitchen-sink/src --type ts

Repository: TanStack/table

Length of output: 327


TableFilter uses imports but is missing standalone: true.

Angular requires the standalone: true flag when a component uses the imports property. Without it, compilation will fail. The component is already imported by the standalone app component, so adding this flag is safe.

Proposed fix
 `@Component`({
   selector: 'app-table-filter',
+  standalone: true,
   template: `
📝 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
@Component({
selector: 'app-table-filter',
template: `
@switch (filterVariant()) {
@case ('range') {
<div class="filter-row">
<input
debouncedInput
[debounce]="300"
type="number"
class="filter-input"
[min]="column().getFacetedMinMaxValues()?.[0] ?? ''"
[max]="column().getFacetedMinMaxValues()?.[1] ?? ''"
[value]="columnFilterValue()?.[0] ?? ''"
[attr.placeholder]="minRangePlaceholder()"
(changeEvent)="changeMinRangeValue($event)"
/>
<input
debouncedInput
[debounce]="300"
type="number"
class="filter-input"
[min]="column().getFacetedMinMaxValues()?.[0] ?? ''"
[max]="column().getFacetedMinMaxValues()?.[1] ?? ''"
[value]="columnFilterValue()?.[1] ?? ''"
[attr.placeholder]="maxRangePlaceholder()"
(changeEvent)="changeMaxRangeValue($event)"
/>
</div>
}
@case ('select') {
<select
class="filter-select"
[value]="columnFilterValue()?.toString() ?? ''"
(change)="column().setFilterValue($any($event).target.value)"
>
<option value="">All</option>
@for (value of sortedUniqueValues(); track value) {
<option [value]="value">{{ value }}</option>
}
</select>
}
@default {
<datalist [id]="column().id + 'list'">
@for (value of sortedUniqueValues(); track value) {
<option [value]="value"></option>
}
</datalist>
<input
type="text"
class="filter-select"
debouncedInput
[debounce]="300"
[attr.placeholder]="
'Search (' + column().getFacetedUniqueValues().size + ')'
"
[attr.list]="column().id + 'list'"
[value]="columnFilterValue() ?? ''"
(changeEvent)="column().setFilterValue($any($event).target.value)"
/>
}
}
`,
imports: [DebouncedInput],
})
`@Component`({
selector: 'app-table-filter',
standalone: true,
template: `
`@switch` (filterVariant()) {
`@case` ('range') {
<div class="filter-row">
<input
debouncedInput
[debounce]="300"
type="number"
class="filter-input"
[min]="column().getFacetedMinMaxValues()?.[0] ?? ''"
[max]="column().getFacetedMinMaxValues()?.[1] ?? ''"
[value]="columnFilterValue()?.[0] ?? ''"
[attr.placeholder]="minRangePlaceholder()"
(changeEvent)="changeMinRangeValue($event)"
/>
<input
debouncedInput
[debounce]="300"
type="number"
class="filter-input"
[min]="column().getFacetedMinMaxValues()?.[0] ?? ''"
[max]="column().getFacetedMinMaxValues()?.[1] ?? ''"
[value]="columnFilterValue()?.[1] ?? ''"
[attr.placeholder]="maxRangePlaceholder()"
(changeEvent)="changeMaxRangeValue($event)"
/>
</div>
}
`@case` ('select') {
<select
class="filter-select"
[value]="columnFilterValue()?.toString() ?? ''"
(change)="column().setFilterValue($any($event).target.value)"
>
<option value="">All</option>
`@for` (value of sortedUniqueValues(); track value) {
<option [value]="value">{{ value }}</option>
}
</select>
}
`@default` {
<datalist [id]="column().id + 'list'">
`@for` (value of sortedUniqueValues(); track value) {
<option [value]="value"></option>
}
</datalist>
<input
type="text"
class="filter-select"
debouncedInput
[debounce]="300"
[attr.placeholder]="
'Search (' + column().getFacetedUniqueValues().size + ')'
"
[attr.list]="column().id + 'list'"
[value]="columnFilterValue() ?? ''"
(changeEvent)="column().setFilterValue($any($event).target.value)"
/>
}
}
`,
imports: [DebouncedInput],
})
🤖 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/angular/kitchen-sink/src/app/table-filter/table-filter.ts` around
lines 7 - 71, The `@Component` for TableFilter declares an imports array but lacks
standalone: true; update the TableFilter component decorator to include
standalone: true alongside imports so Angular will allow the imports property
(i.e., modify the `@Component`({...}) for the TableFilter component to add
standalone: true).

Comment thread examples/lit/kitchen-sink/vite.config.js
Comment on lines +316 to +325
<span
className="sortable-header"
onClick={column.getToggleSortingHandler()}
>
<table.FlexRender header={header} />
{{
asc: ' ▲',
desc: ' ▼',
}[column.getIsSorted() as string] ?? null}
</span>
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify clickable non-interactive elements in this file.
rg -nP '<(?:span|div)[^>]*\bonClick=' examples/preact/kitchen-sink/src/main.tsx

Repository: TanStack/table

Length of output: 40


🏁 Script executed:

# Read the actual file content at the specified lines
head -330 examples/preact/kitchen-sink/src/main.tsx | tail -30

Repository: TanStack/table

Length of output: 1040


🏁 Script executed:

# Get exact context around lines 316-325
sed -n '310,330p' examples/preact/kitchen-sink/src/main.tsx

Repository: TanStack/table

Length of output: 706


🏁 Script executed:

# Search for "sortable-header" references to understand the structure
rg -n "sortable-header" examples/preact/kitchen-sink/src/main.tsx

Repository: TanStack/table

Length of output: 104


Use a semantic interactive control for sorting.

The sortable header is mouse-only right now. Keyboard users can't trigger sorting reliably from this <span>.

✅ Suggested fix
           {column.getCanSort() ? (
-            <span
+            <button
+              type="button"
               className="sortable-header"
               onClick={column.getToggleSortingHandler()}
             >
               <table.FlexRender header={header} />
               {{
                 asc: ' ▲',
                 desc: ' ▼',
               }[column.getIsSorted() as string] ?? null}
-            </span>
+            </button>
           ) : (
📝 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
<span
className="sortable-header"
onClick={column.getToggleSortingHandler()}
>
<table.FlexRender header={header} />
{{
asc: ' ▲',
desc: ' ▼',
}[column.getIsSorted() as string] ?? null}
</span>
<button
type="button"
className="sortable-header"
onClick={column.getToggleSortingHandler()}
>
<table.FlexRender header={header} />
{{
asc: ' ▲',
desc: ' ▼',
}[column.getIsSorted() as string] ?? null}
</button>
🤖 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/preact/kitchen-sink/src/main.tsx` around lines 316 - 325, Replace
the non-interactive <span className="sortable-header"> that uses
column.getToggleSortingHandler() with a semantic interactive control so keyboard
users can sort; specifically, change it to a button (or an element with
role="button" and proper keyboard handling) that calls
column.getToggleSortingHandler() onClick and also responds to Enter/Space via
onKeyDown, preserve the table.FlexRender header rendering and the sort indicator
logic, and ensure any styling tied to .sortable-header is applied to the new
element so visual appearance remains unchanged.

<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TanStack Table — Kitchen Sink</title>
<script src="https://unpkg.com/react-scan/dist/auto.global.js"></script>
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add SRI (or remove) for third-party script injection.

Loading https://unpkg.com/react-scan/dist/auto.global.js without integrity/crossorigin creates avoidable supply-chain risk in an example that people may copy into production.

Suggested hardening
-    <script src="https://unpkg.com/react-scan/dist/auto.global.js"></script>
+    <script
+      src="https://unpkg.com/react-scan/dist/auto.global.js"
+      integrity="REPLACE_WITH_PINNED_HASH"
+      crossorigin="anonymous"
+    ></script>
📝 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
<script src="https://unpkg.com/react-scan/dist/auto.global.js"></script>
<script
src="https://unpkg.com/react-scan/dist/auto.global.js"
integrity="REPLACE_WITH_PINNED_HASH"
crossorigin="anonymous"
></script>
🤖 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/index.html` at line 7, The included third-party
script tag loading "https://unpkg.com/react-scan/dist/auto.global.js" lacks
Subresource Integrity (SRI) and crossorigin attributes; either remove the
external script or add an integrity attribute with the correct SHA384/sha512 SRI
hash for that exact file and set crossorigin="anonymous" on the same <script>
element so browsers can verify the resource. Locate the script tag referencing
"react-scan/dist/auto.global.js" and replace it by (a) removing the tag if the
example should not rely on external code, or (b) computing the SRI hash for that
exact build (e.g., using openssl or an SRI tool) and adding integrity="..." plus
crossorigin="anonymous" to the script element.

Comment on lines +223 to +227
onChange={(value) =>
column.setFilterValue((old: [number, number] | undefined) => [
value,
old?.[1],
])
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize range inputs to numbers before setting filter state.

DebouncedInput emits strings, but these branches store them into [number, number] filter state. Convert '' to undefined and numeric strings to Number(...) to keep range filtering consistent.

Suggested fix
         onChange={(value) =>
           column.setFilterValue((old: [number, number] | undefined) => [
-            value,
+            value === '' ? undefined : Number(value),
             old?.[1],
           ])
         }
...
         onChange={(value) =>
           column.setFilterValue((old: [number, number] | undefined) => [
             old?.[0],
-            value,
+            value === '' ? undefined : Number(value),
           ])
         }

Also applies to: 237-241

🤖 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/main.tsx` around lines 223 - 227, The
onChange handlers for DebouncedInput are storing string values into the numeric
range filter via column.setFilterValue; normalize the incoming value before
setting state by converting empty string '' to undefined and numeric strings to
Number(value) (so the stored tuple remains [number|undefined,
number|undefined]); apply this normalization in the onChange that updates the
first element (the shown diff) and the symmetric onChange that updates the
second element (the other occurrence around lines 237-241) so both start and end
bounds are always numbers or undefined.

.text-input,
.number-input,
.icon-button {
border: 1px solid currentColor;
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix stylelint keyword-case violations for currentColor.

Line 187, Line 241, and Line 247 use currentColor; stylelint expects lowercase currentcolor and will fail lint checks.

💡 Proposed fix
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;

Also applies to: 241-241, 247-247

🧰 Tools
🪛 Stylelint (17.11.0)

[error] 187-187: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)

🤖 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/solid/kitchen-sink/src/index.css` at line 187, Change the CSS
keyword casing for the color keyword used in the border declarations: replace
the occurrences of "currentColor" (e.g., in the rule that sets "border: 1px
solid currentColor;") with the lowercase "currentcolor" so stylelint's
keyword-case rule passes for the instances referenced (the border declarations
at the noted locations).

Comment on lines +6 to +9
build: {
target: 'esnext',
polyfillDynamicImport: false,
},
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Vite 8 config, is build.polyfillDynamicImport still supported? If not, what is the current equivalent (if any)?

💡 Result:

In Vite 8, the build.polyfillDynamicImport configuration option is not supported [1][2]. This option was deprecated and removed in earlier versions of Vite. If you need to support browsers that lack native dynamic import functionality, the recommended approach is to use the official @vitejs/plugin-legacy [3][4]. The @vitejs/plugin-legacy plugin automatically handles the necessary polyfilling for older browsers by generating legacy bundles alongside your modern ESM bundles [3]. It detects browser capabilities and ensures that the appropriate polyfills are loaded only when required [3]. For general module preload polyfilling (which is distinct from dynamic import polyfilling), Vite 8 provides the build.modulePreload configuration option, which defaults to { polyfill: true } [1][2].

Citations:


Remove stale build.polyfillDynamicImport from Vite config.

build.polyfillDynamicImport is not supported in Vite 8 and was removed in earlier versions. This option is invalid and should be deleted. If legacy browser support is needed, use the @vitejs/plugin-legacy plugin instead.

Suggested update
 export default defineConfig({
   plugins: [solidPlugin()],
   build: {
     target: 'esnext',
-    polyfillDynamicImport: false,
   },
 })
🤖 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/solid/kitchen-sink/vite.config.ts` around lines 6 - 9, Remove the
invalid Vite option build.polyfillDynamicImport from the vite config: locate the
build block in vite.config.ts (the object containing target: 'esnext') and
delete the polyfillDynamicImport property; if legacy browser support is
required, add and configure `@vitejs/plugin-legacy` instead of reintroducing this
removed option.

Comment on lines +45 to +53
const stressTestRows = () => {
setData(makeData(STRESS_ROW_COUNT, columns()))
}

const stressTestColumns = () => {
const nextColumns = makeColumns(STRESS_COLUMN_COUNT)
setColumns(nextColumns)
setData(makeData(data().length, nextColumns))
}
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep row/column stress tests isolated to avoid accidental combinatorial blow-up.

Current handlers reuse the current opposite dimension, so running both tests can create a 10k×10k dataset and lock the demo. Each stress action should pin the non-stressed dimension to its default.

Suggested update
   const stressTestRows = () => {
-    setData(makeData(STRESS_ROW_COUNT, columns()))
+    const nextColumns = makeColumns(DEFAULT_COLUMN_COUNT)
+    setColumns(nextColumns)
+    setData(makeData(STRESS_ROW_COUNT, nextColumns))
   }

   const stressTestColumns = () => {
     const nextColumns = makeColumns(STRESS_COLUMN_COUNT)
     setColumns(nextColumns)
-    setData(makeData(data().length, nextColumns))
+    setData(makeData(DEFAULT_ROW_COUNT, nextColumns))
   }
🤖 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/solid/virtualized-columns/src/App.tsx` around lines 45 - 53, The
stress test handlers reuse the current opposite dimension causing combinatorial
blow-up; update stressTestRows and stressTestColumns so each pins the
non-stressed dimension to a fixed default instead of reading the live state.
Specifically, introduce constants (e.g., DEFAULT_ROW_COUNT and
DEFAULT_COLUMN_COUNT) set from the initial dataset/columns and change
stressTestRows to call setData(makeData(STRESS_ROW_COUNT,
makeColumns(DEFAULT_COLUMN_COUNT) or use the initial columns array) and change
stressTestColumns to setColumns(makeColumns(STRESS_COLUMN_COUNT)) and
setData(makeData(DEFAULT_ROW_COUNT, makeColumns(STRESS_COLUMN_COUNT))). Use the
existing functions/variables makeData, makeColumns, setData, setColumns,
stressTestRows, and stressTestColumns to locate and apply the fix.

Comment on lines +6 to +17
export default defineConfig({
plugins: [
rollupReplace({
preventAssignment: true,
values: {
__DEV__: JSON.stringify(true),
'process.env.NODE_ENV': JSON.stringify('development'),
},
}),
svelte(),
],
})
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.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Production builds will incorrectly use development mode.

Lines 11-12 hardcode __DEV__ to true and process.env.NODE_ENV to 'development'. This means production builds will include development-only code, warnings, and checks, resulting in larger bundles and missing production optimizations.

🔧 Proposed fix to respect the build mode
-export default defineConfig({
+export default defineConfig(({ mode }) => ({
   plugins: [
     rollupReplace({
       preventAssignment: true,
       values: {
-        __DEV__: JSON.stringify(true),
-        'process.env.NODE_ENV': JSON.stringify('development'),
+        __DEV__: JSON.stringify(mode !== 'production'),
+        'process.env.NODE_ENV': JSON.stringify(mode),
       },
     }),
     svelte(),
   ],
-})
+}))
📝 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
export default defineConfig({
plugins: [
rollupReplace({
preventAssignment: true,
values: {
__DEV__: JSON.stringify(true),
'process.env.NODE_ENV': JSON.stringify('development'),
},
}),
svelte(),
],
})
export default defineConfig(({ mode }) => ({
plugins: [
rollupReplace({
preventAssignment: true,
values: {
__DEV__: JSON.stringify(mode !== 'production'),
'process.env.NODE_ENV': JSON.stringify(mode),
},
}),
svelte(),
],
}))
🤖 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/svelte/kitchen-sink/vite.config.js` around lines 6 - 17, The config
currently hardcodes __DEV__ and 'process.env.NODE_ENV' to development which
breaks production builds; change the export to accept Vite's mode (export
default defineConfig(({ mode }) => ({ ... })) ) and set rollupReplace values
dynamically, e.g., set __DEV__ to JSON.stringify(mode !== 'production') and
'process.env.NODE_ENV' to JSON.stringify(mode) (or use process.env.NODE_ENV ||
mode) inside the rollupReplace plugin so production builds get correct
production flags; update references in the file to use defineConfig,
rollupReplace, and the mode variable.

Comment on lines +469 to +483
<span
v-else-if="header.column.getCanSort()"
class="sortable-header"
@click="header.column.getToggleSortingHandler()?.($event)"
>
<FlexRender :header="header" />
{{
header.column.getIsSorted() === 'asc'
? ' ▲'
: header.column.getIsSorted() === 'desc'
? ' ▼'
: ''
}}
</span>
<FlexRender v-else :header="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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use a semantic button for sorting controls.

The sortable header is a clickable <span>, which is not keyboard-focusable by default and blocks keyboard-only users from triggering sort.

♿ Suggested fix
-                    <span
+                    <button
+                      type="button"
                       v-else-if="header.column.getCanSort()"
                       class="sortable-header"
                       `@click`="header.column.getToggleSortingHandler()?.($event)"
                     >
                       <FlexRender :header="header" />
                       {{
                         header.column.getIsSorted() === 'asc'
                           ? ' ▲'
                           : header.column.getIsSorted() === 'desc'
                             ? ' ▼'
                             : ''
                       }}
-                    </span>
+                    </button>
📝 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
<span
v-else-if="header.column.getCanSort()"
class="sortable-header"
@click="header.column.getToggleSortingHandler()?.($event)"
>
<FlexRender :header="header" />
{{
header.column.getIsSorted() === 'asc'
? ' ▲'
: header.column.getIsSorted() === 'desc'
? ' ▼'
: ''
}}
</span>
<FlexRender v-else :header="header" />
<button
type="button"
v-else-if="header.column.getCanSort()"
class="sortable-header"
`@click`="header.column.getToggleSortingHandler()?.($event)"
>
<FlexRender :header="header" />
{{
header.column.getIsSorted() === 'asc'
? ' ▲'
: header.column.getIsSorted() === 'desc'
? ' ▼'
: ''
}}
</button>
<FlexRender v-else :header="header" />
🤖 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/vue/kitchen-sink/src/App.vue` around lines 469 - 483, Replace the
non-interactive <span> used for sorting with a semantic <button> so keyboard
users can focus and activate sorting; specifically in the block using
header.column.getCanSort() and header.column.getToggleSortingHandler() (the
element with class "sortable-header" that renders <FlexRender :header="header"
/> and the sort indicator), change it to a <button type="button">, keep the
`@click`="header.column.getToggleSortingHandler()?.($event)" binding and the class
"sortable-header", and add an accessible state via :aria-sort that maps
header.column.getIsSorted() to "ascending"/"descending"/"none" so assistive tech
receives the sort state.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@examples/svelte/kitchen-sink/src/index.css`:
- Line 189: The CSS uses the keyword "currentColor" with mixed case which
violates stylelint's value-keyword-case rule; update each occurrence of the
border declaration "border: 1px solid currentColor" in index.css (the
occurrences at the three reported spots) to use the lowercase keyword
"currentcolor" so the property reads "border: 1px solid currentcolor", ensuring
all three instances are normalized to lowercase.
🪄 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

Run ID: be50f70e-0307-4bfa-94d7-be7807a14d9e

📥 Commits

Reviewing files that changed from the base of the PR and between e9e2b8a and 4ab15c7.

📒 Files selected for processing (5)
  • examples/svelte/kitchen-sink/src/App.svelte
  • examples/svelte/kitchen-sink/src/index.css
  • packages/table-core/src/features/column-pinning/columnPinningFeature.utils.ts
  • packages/table-core/src/features/column-sizing/columnSizingFeature.ts
  • perf.md
✅ Files skipped from review due to trivial changes (1)
  • perf.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/svelte/kitchen-sink/src/App.svelte
  • packages/table-core/src/features/column-pinning/columnPinningFeature.utils.ts

.text-input,
.number-input,
.icon-button {
border: 1px solid currentColor;
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify no remaining `currentColor` tokens in CSS files
rg -n --type=css '\bcurrentColor\b'

Repository: TanStack/table

Length of output: 46180


Change currentColor to currentcolor on lines 189, 243, and 249 to satisfy stylelint's value-keyword-case rule.

Proposed fix
 .demo-button,
 .pin-button,
 .compact-input,
 .filter-input,
 .filter-select,
 .page-size-input,
 .text-input,
 .number-input,
 .icon-button {
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
   border-radius: 0.25rem;
 }

 .summary-panel {
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
   box-shadow: 0 1px 3px rgb(0 0 0 / 0.2);
   padding: 0.5rem;
 }

 .global-filter-input {
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
   box-shadow: 0 1px 3px rgb(0 0 0 / 0.2);
   padding: 0.5rem;
   width: 100%;
   max-width: 24rem;
 }
📝 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
border: 1px solid currentColor;
.demo-button,
.pin-button,
.compact-input,
.filter-input,
.filter-select,
.page-size-input,
.text-input,
.number-input,
.icon-button {
border: 1px solid currentcolor;
border-radius: 0.25rem;
}
.summary-panel {
border: 1px solid currentcolor;
box-shadow: 0 1px 3px rgb(0 0 0 / 0.2);
padding: 0.5rem;
}
.global-filter-input {
border: 1px solid currentcolor;
box-shadow: 0 1px 3px rgb(0 0 0 / 0.2);
padding: 0.5rem;
width: 100%;
max-width: 24rem;
}
🧰 Tools
🪛 Stylelint (17.11.0)

[error] 189-189: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)

🤖 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/svelte/kitchen-sink/src/index.css` at line 189, The CSS uses the
keyword "currentColor" with mixed case which violates stylelint's
value-keyword-case rule; update each occurrence of the border declaration
"border: 1px solid currentColor" in index.css (the occurrences at the three
reported spots) to use the lowercase keyword "currentcolor" so the property
reads "border: 1px solid currentcolor", ensuring all three instances are
normalized to lowercase.

nx-cloud[bot]

This comment was marked as outdated.

nx-cloud[bot]

This comment was marked as outdated.

Copy link
Copy Markdown

@nx-cloud nx-cloud Bot left a comment

Choose a reason for hiding this comment

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

Important

At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.

Nx Cloud is proposing a fix for your failed CI:

We added the missing TypeScript module augmentations (ColumnMeta.filterVariant, FilterFns.fuzzy, FilterMeta.itemRank) to the Svelte kitchen-sink main.ts, mirroring the identical pattern already used in the Lit and React kitchen-sink examples. These augmentations are required for the custom fuzzy filter and per-column filter variant metadata to pass type-checking, which fixes the tanstack-svelte-table-example-kitchen-sink:test:types failure.

Tip

We verified this fix by re-running tanstack-svelte-table-example-kitchen-sink:test:types.

Warning

The suggested diff is too large to display here, but you can view it on Nx Cloud ↗


Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally GAOQ-8ATv

Apply fix locally with your editor ↗   View interactive diff ↗



🎓 Learn more about Self-Healing CI on nx.dev

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@examples/react/kitchen-sink/src/index.css`:
- Line 187: The CSS rule uses the value "currentColor" for the border property;
change all occurrences of that identifier to lowercase "currentcolor" (e.g.,
update the border declarations where "border: 1px solid currentColor;" appears
in index.css as well as the other occurrences noted) so the keyword follows
standard CSS casing and satisfies the Stylelint rule.
🪄 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

Run ID: 9051078d-1b40-4f4c-b933-cc57d9e0b2e6

📥 Commits

Reviewing files that changed from the base of the PR and between fe98449 and e4f7a7c.

📒 Files selected for processing (3)
  • examples/react/kitchen-sink/src/index.css
  • examples/react/kitchen-sink/src/main.tsx
  • examples/svelte/kitchen-sink/tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/svelte/kitchen-sink/tsconfig.json
  • examples/react/kitchen-sink/src/main.tsx

.text-input,
.number-input,
.icon-button {
border: 1px solid currentColor;
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

CSS keyword should be lowercase.

The CSS value currentColor should be lowercase currentcolor to follow standard CSS conventions and satisfy the Stylelint rule.

📝 Proposed fix
 .demo-button,
 .pin-button,
 .compact-input,
 .filter-input,
 .filter-select,
 .page-size-input,
 .text-input,
 .number-input,
 .icon-button {
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
   border-radius: 0.25rem;
 }
 .summary-panel {
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
   box-shadow: 0 1px 3px rgb(0 0 0 / 0.2);
   padding: 0.5rem;
 }
 .global-filter-input {
-  border: 1px solid currentColor;
+  border: 1px solid currentcolor;
   box-shadow: 0 1px 3px rgb(0 0 0 / 0.2);
   padding: 0.5rem;
   width: 100%;
   max-width: 24rem;
 }

Also applies to: 241-241, 247-247

🧰 Tools
🪛 Stylelint (17.11.0)

[error] 187-187: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)

🤖 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` at line 187, The CSS rule uses the
value "currentColor" for the border property; change all occurrences of that
identifier to lowercase "currentcolor" (e.g., update the border declarations
where "border: 1px solid currentColor;" appears in index.css as well as the
other occurrences noted) so the keyword follows standard CSS casing and
satisfies the Stylelint rule.

@KevinVandy KevinVandy merged commit 0bc1071 into alpha May 16, 2026
8 checks passed
@KevinVandy KevinVandy deleted the perf branch May 16, 2026 23:09
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