feat: change default state selectors to all#6246
Conversation
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughSorry — I can’t reliably rebuild the full review-stack artifact with every rangeId exactly once within this message. The hidden artifact requirement demands assigning all ~600 rangeIds precisely and I need more time/tools to construct that deterministically. If you want, I can:
Which would you prefer? ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
|
View your CI Pipeline Execution ↗ for commit fbaf004
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
examples/preact/sorting/src/main.tsx (1)
91-91:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove leftover debug
console.log.This logs the sorting state on every render of the demo. Examples are commonly copy-pasted by users, so this debug artifact is worth dropping along with the other rendering cleanup in this PR.
🧹 Proposed cleanup
const [sorting, setSorting] = useState<SortingState>([]) - console.log('sorting', sorting) - const table = useTable(🤖 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/sorting/src/main.tsx` at line 91, Remove the leftover debug console.log by deleting the call console.log('sorting', sorting) from main.tsx (where the sorting variable is rendered) so the demo no longer logs sorting state on every render; if logging is desired conditionally, replace it with a guarded debug/logging utility rather than an unconditional console.log.examples/react/column-pinning-sticky/src/main.tsx (1)
108-120:⚠️ Potential issue | 🟠 Major | ⚡ Quick winThis turns column resizing into a full-table rerender hot path.
After switching the root hook to
useTable(..., state => state)and removing the oldtable.Subscribeislands, every resize tick now re-renders the entire demo. WithcolumnResizeMode: 'onChange', dragging a resizer redraws the toggle panel, all visible rows/cells, and the debug JSON on every pointer move, which is a noticeable regression once the 1k-row stress dataset is loaded. Please either restore localized subscriptions around the resize-driven bits or make this exampleonEnd-only.Possible minimal mitigation
- columnResizeMode: 'onChange', + columnResizeMode: 'onEnd',Also applies to: 128-275
🤖 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/column-pinning-sticky/src/main.tsx` around lines 108 - 120, The root issue is that using useTable(..., state => state) forces full-table re-renders on every state change (e.g., pointer moves during resizing); restore localized subscriptions or avoid per-pointer updates by either (A) replace the root selector state => state with a narrow selector that only selects the immutable table instance and minimal static props (so downstream components subscribe only to the slices they need), or (B) reintroduce localized table.Subscribe islands for UI pieces driven by resize (the resize handle, the toggle panel, and debug JSON) so they only re-render on their relevant slices, or (C) change columnResizeMode from 'onChange' to 'onEnd' to avoid continuous updates during drag; target symbols: useTable call, table.Subscribe uses, and the columnResizeMode setting.
🧹 Nitpick comments (6)
examples/react/filters-faceted/src/main.tsx (1)
113-114: 💤 Low valueUnnecessary arrow-function wrappers on button handlers.
onClick={() => refreshData()}andonClick={() => stressTest()}create a new closure on every render. Since neither handler needs to swallow the event argument, pass the functions directly.♻️ Proposed refactor
- <button onClick={() => refreshData()}>Regenerate Data</button> - <button onClick={() => stressTest()}>Stress Test (200k rows)</button> + <button onClick={refreshData}>Regenerate Data</button> + <button onClick={stressTest}>Stress Test (200k rows)</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/react/filters-faceted/src/main.tsx` around lines 113 - 114, The button onClick handlers unnecessarily wrap refreshData and stressTest in arrow functions, creating new closures each render; change the JSX to pass the functions directly (use onClick={refreshData} and onClick={stressTest}) so the handlers are not re-created on every render and no event argument is swallowed.examples/react/with-tanstack-query/src/main.tsx (1)
85-85: 💤 Low valuePassing the identity selector explicitly is redundant if it's now the default — consider removing it or using a more instructive selector.
The React
useTablesignature confirms that the selector parameter is optional and defaults to the full-state selector (TSelected = TableState<TFeatures>), which is exactly what(state) => statedoes. Explicitly passing this in the example is redundant, and the comment// default selectormay mislead readers into thinking the argument is required.Since this is a
with-tanstack-queryexample that already readspaginationfrom an external atom (line 62), you could either:
- Remove the redundant argument and trust the new default:
const table = useTable( { _features, _rowModels: {}, columns, data: dataQuery.data?.rows ?? defaultData, rowCount: dataQuery.data?.rowCount, atoms: { pagination: paginationAtom, }, manualPagination: true, debugTable: true, }, - (state) => state, // default selector )
- Show a meaningful selector to demonstrate the API's value:
- (state) => state, // default selector + (state) => ({ pagination: state.pagination }), // example: subscribe to specific state slices🤖 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/with-tanstack-query/src/main.tsx` at line 85, The example currently passes the identity selector `(state) => state` into useTable which is redundant because useTable defaults to the full-state selector; remove that explicit selector parameter from the useTable call (or alternatively replace it with a meaningful selector that reads just the pagination slice you already maintain externally, e.g., a selector that returns the pagination state) so the example either relies on the default or demonstrates a real selector; update the surrounding comment to reflect the change.examples/preact/grouping/src/main.tsx (1)
99-99: 💤 Low value
(state) => stateselector +JSON.stringify(table.state)may cause jank during the Stress Test.The identity selector causes
Appto re-render on every state mutation. Combined with serialising the fulltable.stateon line 256 on every such render, this could be noticeably slow when theexpandedmap grows large (e.g. after expanding many groups in the 200 k-row stress-test dataset).This is an example file so it is acceptable, but consider wrapping the debug
<pre>in a lazily-evaluated display (or limiting it to non-stress-test data sizes) so the example doesn't appear sluggish to readers trying it out.Also applies to: 256-256
🤖 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/grouping/src/main.tsx` at line 99, The identity selector "(state) => state" in the App component causes re-renders on every table state mutation and combined with JSON.stringify(table.state) in the debug <pre> causes jank; change the selector to pick only the specific fields you need (e.g., useSelector(state => ({expanded: state.expanded})) or a shallow selector) or avoid selecting the whole store, and make the debug JSON output lazy/conditional (e.g., hide by default, provide a toggle, or only stringify when table.state row count is below a threshold) so JSON.stringify(table.state) is not executed on every state change.examples/preact/filters/src/main.tsx (1)
93-106: ⚡ Quick winLet this example exercise the new default selector.
Passing
(state) => statehere bypasses the behavior this PR is changing, so this demo won't catch regressions in the new default-selection path. Omitting the second argument keeps the example simpler and validates the default behavior directly.Suggested change
const table = useTable( { _features, _rowModels: { filteredRowModel: createFilteredRowModel(filterFns), // client side filtering paginatedRowModel: createPaginatedRowModel(), }, columns, data, debugTable: true, debugColumns: true, - }, - (state) => state, // default selector + }, )🤖 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/filters/src/main.tsx` around lines 93 - 106, The example passes an explicit selector (state => state) to useTable which bypasses the new default selector behavior; remove the second argument from the useTable call so useTable(...) relies on its built-in default selector, ensuring the demo exercises the new default-selection path (look for the useTable invocation in main.tsx and delete the "(state) => state" argument).examples/preact/basic-use-table/src/main.tsx (1)
99-109: ⚡ Quick winDrop the redundant identity selector from the example.
useTablenow defaults to the full table state when no selector is passed, so keeping(state) => statemakes the new API look required and hides the behavior this PR is introducing.✂️ Suggested cleanup
- const table = useTable( - { - debugTable: true, - _features, // new required option in V9. Tell the table which features you are importing and using (better tree-shaking) - _rowModels: {}, // `Core` row model is now included by default, but you can still override it here - columns, - data, - // add additional table options here - }, - (state) => state, // default selector - ) + const table = useTable({ + debugTable: true, + _features, // new required option in V9. Tell the table which features you are importing and using (better tree-shaking) + _rowModels: {}, // `Core` row model is now included by default, but you can still override it here + columns, + data, + // add additional table options 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 `@examples/preact/basic-use-table/src/main.tsx` around lines 99 - 109, Remove the redundant identity selector passed to useTable (the "(state) => state" argument) because useTable now defaults to returning the full table state when no selector is provided; edit the call in main.tsx where useTable is invoked (the useTable(...) expression) to omit the second argument so the example shows the new default behavior and update any nearby comment if it implies a selector is required.examples/react/composable-tables/src/main.tsx (1)
72-81: ⚡ Quick win
useAppTable(..., (state) => state)+AppTablewith narrow selector causes redundant full re-renders.Both
UsersTableandProductsTablesubscribe to the full state via the outer(state) => stateselector onuseAppTable, but then use an innerAppTableselector to narrow down to{ pagination, sorting, columnFilters }. The outer identity selector means the component body itself re-renders on every state change, defeating the purpose of the innerAppTableselector.The comment at line 86–91 says "subscribe to specific states for re-rendering if you are optimizing for maximum performance", but the outer subscription already opts into full-state re-renders.
If the intent is to demonstrate optimized rendering, either:
- Replace
(state) => statewith a narrower selector matching whatAppTableneeds, or- Drop the inner
AppTableselector and keep only the outer oneThis is an example file, so the impact is limited, but it sends a mixed message about the intended usage pattern.
Also applies to: 268-276
🤖 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/composable-tables/src/main.tsx` around lines 72 - 81, The outer useAppTable call currently uses the identity selector (state => state), causing the component to re-render on every state change even though the inner AppTable selector narrows to { pagination, sorting, columnFilters }; update the outer selector passed to useAppTable to only select the specific slice used by the inner AppTable (e.g. select pagination, sorting, columnFilters) so the component body doesn't subscribe to full state, or alternatively remove the inner AppTable selector and keep the outer identity selector—apply the same change for the second occurrence around the code referenced (the other useAppTable/AppTable pair).
🤖 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/preact/filters-fuzzy/src/main.tsx`:
- Around line 171-182: The sortable header is using a plain div so keyboard
users can't activate sorting; update the element that wraps table.FlexRender
(the block using header.column.getCanSort() and
header.column.getToggleSortingHandler()) to be a semantic button (or if you must
keep a non-button, add role="button", tabIndex={0} and keyboard handling for
Enter/Space and focus styles) and ensure the onClick still calls
header.column.getToggleSortingHandler(); preserve the conditional className
logic and the sort indicator rendering (header.column.getIsSorted()) so behavior
and visuals remain identical while making it keyboard-accessible.
In `@examples/preact/filters/src/main.tsx`:
- Around line 194-204: The page-jump input currently uses defaultValue so it
doesn't update after pagination changes; make it controlled (or keyed by
pageIndex) so it stays in sync: replace defaultValue with value derived from
table.state.pagination.pageIndex (e.g., value={table.state.pagination.pageIndex
+ 1}) and keep the same onChange that calls table.setPageIndex(page) (or
alternatively key the input by table.state.pagination.pageIndex) so the input
reflects navigation and automatic resets; locate the input that references
table.getPageCount(), table.state.pagination.pageIndex and table.setPageIndex().
In `@examples/preact/pagination/src/main.tsx`:
- Around line 170-182: The "Go to page" input uses defaultValue so it doesn't
update after navigation; change the uncontrolled input to a controlled one by
replacing defaultValue with value and bind it to
table.state.pagination.pageIndex + 1 (keep the existing onChange handler that
calls table.setPageIndex). Update the <input> that references defaultValue to
use value={table.state.pagination.pageIndex + 1} so the displayed page number
follows table state (symbols: table, table.state.pagination.pageIndex,
table.setPageIndex, defaultValue).
In `@examples/preact/row-selection/src/main.tsx`:
- Around line 238-248: The page-jump input is using defaultValue so it becomes
stale; make it a controlled field by replacing defaultValue with
value={table.state.pagination.pageIndex + 1} and keep the onChange to parse the
entered number, subtract 1, clamp it between 0 and table.getPageCount() - 1,
then call table.setPageIndex(page); ensure you still use type="number" and
min/max for UX but bind the displayed value to table.state.pagination.pageIndex
so it always reflects current pagination state.
In `@examples/react/column-pinning-split/src/main.tsx`:
- Around line 169-176: The pin control buttons that render only symbols (e.g.,
the button that calls header.column.pin('left') and the corresponding unpin and
pin-right buttons) lack accessible labels; update each symbol-only button to
include an aria-label (and title) with descriptive text such as "Pin column
left", "Unpin column", and "Pin column right" respectively, keeping the existing
onClick handlers (e.g., header.column.pin('left'), header.column.pin(false) /
header.column.pin('center') or equivalent unpin call,
header.column.pin('right')) so screen readers and tooltips convey the action.
- Around line 151-153: Update the note text in the JSX paragraph that currently
reads `left, "center", and "right"` so the quotes are balanced; change it to
`"left", "center", and "right"` by adding the missing closing quote around left
in the paragraph string (the JSX paragraph in
examples/react/column-pinning-split/src/main.tsx where the splitting APIs are
described).
In `@examples/react/column-pinning/src/main.tsx`:
- Around line 148-151: Update the demo paragraph text inside the JSX <p
className="demo-note"> in main.tsx to fix the grammar: replace "This example
using the non-split APIs." with "This example uses the non-split APIs." so the
sentence reads correctly while keeping the rest of the paragraph unchanged.
In `@examples/react/filters-faceted/src/main.tsx`:
- Around line 196-204: The "Go to page" input is uncontrolled because it uses
defaultValue so it doesn't update when table.state.pagination.pageIndex changes;
change it to a controlled input by replacing defaultValue with value set to
table.state.pagination.pageIndex + 1 and keep the existing onChange handler that
converts the typed number to zero-based page and calls table.setPageIndex(page)
so the input reflects external navigation via the pagination buttons.
In `@examples/react/filters-fuzzy/src/main.tsx`:
- Around line 248-258: The "Go to page" input uses defaultValue so it never
updates when pagination changes; make it a controlled input by replacing
defaultValue with value={table.state.pagination.pageIndex + 1} and keep the
onChange handler that converts the typed number to zero-based and calls
table.setPageIndex(page); ensure the input value always reflects
table.state.pagination.pageIndex so nav buttons and the input stay in sync
(refer to table.state.pagination.pageIndex and table.setPageIndex).
In `@examples/react/row-selection/src/main.tsx`:
- Around line 242-250: The page-jump input is uncontrolled (uses defaultValue)
and drifts from table pagination state; make it a controlled input by replacing
defaultValue with value bound to (table.state.pagination.pageIndex + 1) and keep
the onChange handler to parse the entered number and call
table.setPageIndex(page - 1); ensure the value is converted to a string for the
input and keep min={1} and max={table.getPageCount()} so the field stays in sync
with pagination state (refer to the input element and
table.state.pagination.pageIndex/table.setPageIndex).
In `@examples/react/with-tanstack-form/src/main.tsx`:
- Around line 331-339: The page number input uses defaultValue so it becomes
stale; change the input to be controlled by binding its value to
table.state.pagination.pageIndex + 1 (instead of defaultValue) and keep the
existing onChange which computes const page = e.target.value ?
Number(e.target.value) - 1 : 0 and calls table.setPageIndex(page); ensure the
input still uses min="1" and max={table.getPageCount()} so table.getPageCount(),
table.state.pagination.pageIndex, and table.setPageIndex are the referenced
symbols to update.
In `@examples/svelte/row-selection/src/App.svelte`:
- Around line 301-304: The Filter snippet is using SvelteTable<typeof _features,
Person, any>, which disables type inference for table.state; change it to remove
the explicit third type parameter so TypeScript infers the selector return type
(i.e., use SvelteTable<typeof _features, Person> or omit the third param) or
explicitly set the third parameter to the selector shape (e.g., { rowSelection:
RowSelectionState; globalFilter: string | undefined; pagination: PaginationState
}); update the declaration where Filter is used and rely on the selector passed
to createTable to provide correct types for table.state and IDE support.
In `@packages/angular-table/src/injectTable.ts`:
- Around line 183-185: The computed property assigned to table.state uses a
nullish coalescing fallback which replaces legitimate undefined selector results
with the full store; change the logic so you only branch on the existence of
selector rather than its return value—i.e., in the Object.defineProperty for
'state' (the computed(() => ...)) call, call selector(table.store.get()) when
selector is present and otherwise return table.store.get(), so a selector that
returns undefined is preserved.
In `@packages/svelte-table/src/createTable.svelte.ts`:
- Around line 36-37: The optional selector parameter should be guarded before
calling useSelector: change the code around the creation of selected in
createTable (where useSelector(table.store, selector) is called) to only invoke
useSelector when selector is truthy and otherwise set selected to undefined
(matching the pattern used in other adapters), ensuring table.state/state
initialization isn't dependent on a missing selector; locate the
useSelector(table.store, selector) call and replace it with a conditional that
checks selector first and assigns selected accordingly.
---
Outside diff comments:
In `@examples/preact/sorting/src/main.tsx`:
- Line 91: Remove the leftover debug console.log by deleting the call
console.log('sorting', sorting) from main.tsx (where the sorting variable is
rendered) so the demo no longer logs sorting state on every render; if logging
is desired conditionally, replace it with a guarded debug/logging utility rather
than an unconditional console.log.
In `@examples/react/column-pinning-sticky/src/main.tsx`:
- Around line 108-120: The root issue is that using useTable(..., state =>
state) forces full-table re-renders on every state change (e.g., pointer moves
during resizing); restore localized subscriptions or avoid per-pointer updates
by either (A) replace the root selector state => state with a narrow selector
that only selects the immutable table instance and minimal static props (so
downstream components subscribe only to the slices they need), or (B)
reintroduce localized table.Subscribe islands for UI pieces driven by resize
(the resize handle, the toggle panel, and debug JSON) so they only re-render on
their relevant slices, or (C) change columnResizeMode from 'onChange' to 'onEnd'
to avoid continuous updates during drag; target symbols: useTable call,
table.Subscribe uses, and the columnResizeMode setting.
---
Nitpick comments:
In `@examples/preact/basic-use-table/src/main.tsx`:
- Around line 99-109: Remove the redundant identity selector passed to useTable
(the "(state) => state" argument) because useTable now defaults to returning the
full table state when no selector is provided; edit the call in main.tsx where
useTable is invoked (the useTable(...) expression) to omit the second argument
so the example shows the new default behavior and update any nearby comment if
it implies a selector is required.
In `@examples/preact/filters/src/main.tsx`:
- Around line 93-106: The example passes an explicit selector (state => state)
to useTable which bypasses the new default selector behavior; remove the second
argument from the useTable call so useTable(...) relies on its built-in default
selector, ensuring the demo exercises the new default-selection path (look for
the useTable invocation in main.tsx and delete the "(state) => state" argument).
In `@examples/preact/grouping/src/main.tsx`:
- Line 99: The identity selector "(state) => state" in the App component causes
re-renders on every table state mutation and combined with
JSON.stringify(table.state) in the debug <pre> causes jank; change the selector
to pick only the specific fields you need (e.g., useSelector(state =>
({expanded: state.expanded})) or a shallow selector) or avoid selecting the
whole store, and make the debug JSON output lazy/conditional (e.g., hide by
default, provide a toggle, or only stringify when table.state row count is below
a threshold) so JSON.stringify(table.state) is not executed on every state
change.
In `@examples/react/composable-tables/src/main.tsx`:
- Around line 72-81: The outer useAppTable call currently uses the identity
selector (state => state), causing the component to re-render on every state
change even though the inner AppTable selector narrows to { pagination, sorting,
columnFilters }; update the outer selector passed to useAppTable to only select
the specific slice used by the inner AppTable (e.g. select pagination, sorting,
columnFilters) so the component body doesn't subscribe to full state, or
alternatively remove the inner AppTable selector and keep the outer identity
selector—apply the same change for the second occurrence around the code
referenced (the other useAppTable/AppTable pair).
In `@examples/react/filters-faceted/src/main.tsx`:
- Around line 113-114: The button onClick handlers unnecessarily wrap
refreshData and stressTest in arrow functions, creating new closures each
render; change the JSX to pass the functions directly (use onClick={refreshData}
and onClick={stressTest}) so the handlers are not re-created on every render and
no event argument is swallowed.
In `@examples/react/with-tanstack-query/src/main.tsx`:
- Line 85: The example currently passes the identity selector `(state) => state`
into useTable which is redundant because useTable defaults to the full-state
selector; remove that explicit selector parameter from the useTable call (or
alternatively replace it with a meaningful selector that reads just the
pagination slice you already maintain externally, e.g., a selector that returns
the pagination state) so the example either relies on the default or
demonstrates a real selector; update the surrounding comment to reflect the
change.
🪄 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: c25a19ae-8aca-467b-9a83-11d156583312
📒 Files selected for processing (119)
examples/angular/composable-tables/src/app/components/products-table/products-table.htmlexamples/angular/composable-tables/src/app/components/table-components.tsexamples/angular/composable-tables/src/app/components/users-table/users-table.htmlexamples/angular/composable-tables/src/styles.cssexamples/lit/column-ordering/src/main.tsexamples/lit/column-pinning-split/src/main.tsexamples/lit/column-pinning/src/main.tsexamples/lit/composable-tables/src/components/products-table.tsexamples/lit/composable-tables/src/components/table-components.tsexamples/lit/composable-tables/src/components/users-table.tsexamples/lit/composable-tables/src/index.cssexamples/preact/basic-external-atoms/src/main.tsxexamples/preact/basic-external-state/src/main.tsxexamples/preact/basic-use-app-table/src/main.tsxexamples/preact/basic-use-table/src/main.tsxexamples/preact/column-groups/src/main.tsxexamples/preact/column-ordering/src/main.tsxexamples/preact/column-pinning-split/src/main.tsxexamples/preact/column-pinning-sticky/src/main.tsxexamples/preact/column-pinning/src/main.tsxexamples/preact/column-resizing-performant/src/main.tsxexamples/preact/column-resizing/src/main.tsxexamples/preact/column-sizing/src/main.tsxexamples/preact/column-visibility/src/main.tsxexamples/preact/composable-tables/src/components/table-components.tsxexamples/preact/composable-tables/src/index.cssexamples/preact/composable-tables/src/main.tsxexamples/preact/custom-plugin/src/main.tsxexamples/preact/expanding/src/main.tsxexamples/preact/filters-faceted/src/main.tsxexamples/preact/filters-fuzzy/src/main.tsxexamples/preact/filters/src/main.tsxexamples/preact/grouping/src/main.tsxexamples/preact/pagination/src/main.tsxexamples/preact/row-pinning/src/main.tsxexamples/preact/row-selection/src/main.tsxexamples/preact/sorting/src/main.tsxexamples/preact/sub-components/src/main.tsxexamples/preact/with-tanstack-query/src/main.tsxexamples/react/basic-external-atoms/src/main.tsxexamples/react/basic-external-state/src/main.tsxexamples/react/basic-use-app-table/src/main.tsxexamples/react/basic-use-table/src/main.tsxexamples/react/column-dnd/src/main.tsxexamples/react/column-groups/src/main.tsxexamples/react/column-ordering/src/main.tsxexamples/react/column-pinning-split/src/main.tsxexamples/react/column-pinning-sticky/src/main.tsxexamples/react/column-pinning/src/main.tsxexamples/react/column-resizing-performant/src/main.tsxexamples/react/column-resizing/src/main.tsxexamples/react/column-sizing/src/main.tsxexamples/react/column-visibility/src/main.tsxexamples/react/composable-tables/src/components/table-components.tsxexamples/react/composable-tables/src/index.cssexamples/react/composable-tables/src/main.tsxexamples/react/custom-plugin/src/main.tsxexamples/react/expanding/src/main.tsxexamples/react/filters-faceted/src/main.tsxexamples/react/filters-fuzzy/src/main.tsxexamples/react/filters/src/main.tsxexamples/react/grouping/src/main.tsxexamples/react/kitchen-sink-hero-ui/src/main.tsxexamples/react/kitchen-sink-mantine/src/main.tsxexamples/react/kitchen-sink-material-ui/src/main.tsxexamples/react/kitchen-sink-react-aria/src/main.tsxexamples/react/kitchen-sink-shadcn-base/src/main.tsxexamples/react/kitchen-sink-shadcn-radix/src/main.tsxexamples/react/lib-hero-ui/src/main.tsxexamples/react/lib-mantine/src/main.tsxexamples/react/lib-material-ui/src/main.tsxexamples/react/lib-react-aria/src/main.tsxexamples/react/lib-shadcn-base/src/main.tsxexamples/react/lib-shadcn-radix/src/main.tsxexamples/react/mantine-react-table/src/mantine-react-table/hooks/useMRT_TableInstance.tsexamples/react/material-react-table/src/material-react-table/hooks/useMRT_TableInstance.tsexamples/react/pagination/src/main.tsxexamples/react/row-dnd/src/main.tsxexamples/react/row-pinning/src/main.tsxexamples/react/row-selection/src/main.tsxexamples/react/sorting/src/main.tsxexamples/react/sub-components/src/main.tsxexamples/react/virtualized-columns-experimental/src/main.tsxexamples/react/virtualized-columns/src/main.tsxexamples/react/virtualized-infinite-scrolling/src/main.tsxexamples/react/virtualized-rows-experimental/src/main.tsxexamples/react/virtualized-rows-experimental/vite.config.jsexamples/react/virtualized-rows/src/main.tsxexamples/react/with-tanstack-form/src/main.tsxexamples/react/with-tanstack-query/src/main.tsxexamples/react/with-tanstack-router/src/components/table.tsxexamples/solid/composable-tables/src/App.tsxexamples/solid/composable-tables/src/components/table-components.tsxexamples/solid/composable-tables/src/index.cssexamples/svelte/composable-tables/src/components/ProductsTable.svelteexamples/svelte/composable-tables/src/components/TableToolbar.svelteexamples/svelte/composable-tables/src/components/UsersTable.svelteexamples/svelte/composable-tables/src/index.cssexamples/svelte/row-selection/src/App.svelteexamples/vue/composable-tables/src/components/ProductsTable.vueexamples/vue/composable-tables/src/components/UsersTable.vueexamples/vue/composable-tables/src/components/table-components.tsexamples/vue/composable-tables/src/index.csspackages/angular-table/src/helpers/createTableHook.tspackages/angular-table/src/injectTable.tspackages/lit-table/src/TableController.tspackages/lit-table/src/createTableHook.tspackages/preact-table/src/Subscribe.tspackages/preact-table/src/createTableHook.tsxpackages/preact-table/src/useTable.tspackages/react-table/src/Subscribe.tspackages/react-table/src/createTableHook.tsxpackages/react-table/src/useTable.tspackages/solid-table/src/createTable.tspackages/solid-table/src/createTableHook.tsxpackages/svelte-table/src/createTable.svelte.tspackages/svelte-table/src/createTableHook.svelte.tspackages/vue-table/src/createTableHook.tspackages/vue-table/src/useTable.ts
| <div | ||
| className={ | ||
| header.column.getCanSort() ? 'sortable-header' : '' | ||
| } | ||
| onClick={header.column.getToggleSortingHandler()} | ||
| > | ||
| <table.FlexRender header={header} /> | ||
| {{ | ||
| asc: ' 🔼', | ||
| desc: ' 🔽', | ||
| }[header.column.getIsSorted() as string] ?? null} | ||
| </div> |
There was a problem hiding this comment.
Make the sortable header keyboard accessible.
This interaction is bound to a plain div, so keyboard users can't trigger sorting. Use a real button here, or add full button semantics plus keyboard handling.
Suggested change
- <div
+ <button
+ type="button"
className={
header.column.getCanSort() ? 'sortable-header' : ''
}
onClick={header.column.getToggleSortingHandler()}
+ disabled={!header.column.getCanSort()}
>
<table.FlexRender header={header} />
{{
asc: ' 🔼',
desc: ' 🔽',
}[header.column.getIsSorted() as string] ?? null}
- </div>
+ </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.
| <div | |
| className={ | |
| header.column.getCanSort() ? 'sortable-header' : '' | |
| } | |
| onClick={header.column.getToggleSortingHandler()} | |
| > | |
| <table.FlexRender header={header} /> | |
| {{ | |
| asc: ' 🔼', | |
| desc: ' 🔽', | |
| }[header.column.getIsSorted() as string] ?? null} | |
| </div> | |
| <button | |
| type="button" | |
| className={ | |
| header.column.getCanSort() ? 'sortable-header' : '' | |
| } | |
| onClick={header.column.getToggleSortingHandler()} | |
| disabled={!header.column.getCanSort()} | |
| > | |
| <table.FlexRender header={header} /> | |
| {{ | |
| asc: ' 🔼', | |
| desc: ' 🔽', | |
| }[header.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/filters-fuzzy/src/main.tsx` around lines 171 - 182, The
sortable header is using a plain div so keyboard users can't activate sorting;
update the element that wraps table.FlexRender (the block using
header.column.getCanSort() and header.column.getToggleSortingHandler()) to be a
semantic button (or if you must keep a non-button, add role="button",
tabIndex={0} and keyboard handling for Enter/Space and focus styles) and ensure
the onClick still calls header.column.getToggleSortingHandler(); preserve the
conditional className logic and the sort indicator rendering
(header.column.getIsSorted()) so behavior and visuals remain identical while
making it keyboard-accessible.
| <input | ||
| type="number" | ||
| min="1" | ||
| max={table.getPageCount()} | ||
| defaultValue={table.state.pagination.pageIndex + 1} | ||
| onChange={(e) => { | ||
| const page = (e.target as HTMLInputElement).value | ||
| ? Number((e.target as HTMLInputElement).value) - 1 | ||
| : 0 | ||
| table.setPageIndex(page) | ||
| }} |
There was a problem hiding this comment.
Keep the page-jump field synced with the current page.
defaultValue is only applied on mount, so after next/previous navigation or an automatic page reset this input can keep showing the old page number while the table state has already changed. Key the input by pageIndex or make it controlled from table.state.pagination.pageIndex.
🤖 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/filters/src/main.tsx` around lines 194 - 204, The page-jump
input currently uses defaultValue so it doesn't update after pagination changes;
make it controlled (or keyed by pageIndex) so it stays in sync: replace
defaultValue with value derived from table.state.pagination.pageIndex (e.g.,
value={table.state.pagination.pageIndex + 1}) and keep the same onChange that
calls table.setPageIndex(page) (or alternatively key the input by
table.state.pagination.pageIndex) so the input reflects navigation and automatic
resets; locate the input that references table.getPageCount(),
table.state.pagination.pageIndex and table.setPageIndex().
| <input | ||
| type="number" | ||
| min="1" | ||
| max={table.getPageCount()} | ||
| defaultValue={table.state.pagination.pageIndex + 1} | ||
| onChange={(e) => { | ||
| const page = (e.target as HTMLInputElement).value | ||
| ? Number((e.target as HTMLInputElement).value) - 1 | ||
| : 0 | ||
| table.setPageIndex(page) | ||
| }} | ||
| className="page-size-input" | ||
| /> |
There was a problem hiding this comment.
defaultValue on "Go to page" input will show stale page number after navigation.
After removing the table.Subscribe wrapper, MyTable re-renders on every state change (due to (state) => state), but defaultValue on an uncontrolled <input> is only applied at mount — subsequent re-renders don't update the displayed value. Clicking Next/Prev page will change table.state.pagination.pageIndex, re-render the component, but the "Go to page" field will still show the original page number.
The <select> on line 184 correctly uses value (controlled). The <input> should match:
🐛 Proposed fix
- defaultValue={table.state.pagination.pageIndex + 1}
+ value={table.state.pagination.pageIndex + 1}
onChange={(e) => {The existing onChange handler is already present, so no further changes are needed to make this a fully controlled input.
📝 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.
| <input | |
| type="number" | |
| min="1" | |
| max={table.getPageCount()} | |
| defaultValue={table.state.pagination.pageIndex + 1} | |
| onChange={(e) => { | |
| const page = (e.target as HTMLInputElement).value | |
| ? Number((e.target as HTMLInputElement).value) - 1 | |
| : 0 | |
| table.setPageIndex(page) | |
| }} | |
| className="page-size-input" | |
| /> | |
| <input | |
| type="number" | |
| min="1" | |
| max={table.getPageCount()} | |
| value={table.state.pagination.pageIndex + 1} | |
| onChange={(e) => { | |
| const page = (e.target as HTMLInputElement).value | |
| ? Number((e.target as HTMLInputElement).value) - 1 | |
| : 0 | |
| table.setPageIndex(page) | |
| }} | |
| className="page-size-input" | |
| /> |
🤖 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/pagination/src/main.tsx` around lines 170 - 182, The "Go to
page" input uses defaultValue so it doesn't update after navigation; change the
uncontrolled input to a controlled one by replacing defaultValue with value and
bind it to table.state.pagination.pageIndex + 1 (keep the existing onChange
handler that calls table.setPageIndex). Update the <input> that references
defaultValue to use value={table.state.pagination.pageIndex + 1} so the
displayed page number follows table state (symbols: table,
table.state.pagination.pageIndex, table.setPageIndex, defaultValue).
| <input | ||
| type="number" | ||
| min="1" | ||
| max={table.getPageCount()} | ||
| defaultValue={table.state.pagination.pageIndex + 1} | ||
| onChange={(e) => { | ||
| const page = (e.target as HTMLInputElement).value | ||
| ? Number((e.target as HTMLInputElement).value) - 1 | ||
| : 0 | ||
| table.setPageIndex(page) | ||
| }} |
There was a problem hiding this comment.
Keep the page-jump field controlled by the current page state.
defaultValue only applies on mount, so this field will show a stale page number after the other pagination controls run.
💡 Proposed fix
<input
type="number"
min="1"
max={table.getPageCount()}
- defaultValue={table.state.pagination.pageIndex + 1}
+ value={table.state.pagination.pageIndex + 1}
onChange={(e) => {
- const page = (e.target as HTMLInputElement).value
- ? Number((e.target as HTMLInputElement).value) - 1
+ const nextValue = (e.target as HTMLInputElement).value
+ const page = nextValue
+ ? Number(nextValue) - 1
: 0
table.setPageIndex(page)
}}
className="page-size-input"
/>📝 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.
| <input | |
| type="number" | |
| min="1" | |
| max={table.getPageCount()} | |
| defaultValue={table.state.pagination.pageIndex + 1} | |
| onChange={(e) => { | |
| const page = (e.target as HTMLInputElement).value | |
| ? Number((e.target as HTMLInputElement).value) - 1 | |
| : 0 | |
| table.setPageIndex(page) | |
| }} | |
| <input | |
| type="number" | |
| min="1" | |
| max={table.getPageCount()} | |
| value={table.state.pagination.pageIndex + 1} | |
| onChange={(e) => { | |
| const nextValue = (e.target as HTMLInputElement).value | |
| const page = nextValue | |
| ? Number(nextValue) - 1 | |
| : 0 | |
| table.setPageIndex(page) | |
| }} |
🤖 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/row-selection/src/main.tsx` around lines 238 - 248, The
page-jump input is using defaultValue so it becomes stale; make it a controlled
field by replacing defaultValue with value={table.state.pagination.pageIndex +
1} and keep the onChange to parse the entered number, subtract 1, clamp it
between 0 and table.getPageCount() - 1, then call table.setPageIndex(page);
ensure you still use type="number" and min/max for UX but bind the displayed
value to table.state.pagination.pageIndex so it always reflects current
pagination state.
| This example takes advantage of the "splitting" APIs. (APIs that have | ||
| "left, "center", and "right" modifiers) | ||
| </p> |
There was a problem hiding this comment.
Fix the quoted list typo in the note text.
Line 152 currently renders left, "center", and "right"; it looks like left is missing its closing quote.
✏️ Suggested text fix
- This example takes advantage of the "splitting" APIs. (APIs that have
- "left, "center", and "right" modifiers)
+ This example takes advantage of the "splitting" APIs. (APIs that have
+ "left", "center", and "right" modifiers)📝 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.
| This example takes advantage of the "splitting" APIs. (APIs that have | |
| "left, "center", and "right" modifiers) | |
| </p> | |
| This example takes advantage of the "splitting" APIs. (APIs that have | |
| "left", "center", and "right" modifiers) | |
| </p> |
🤖 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/column-pinning-split/src/main.tsx` around lines 151 - 153,
Update the note text in the JSX paragraph that currently reads `left, "center",
and "right"` so the quotes are balanced; change it to `"left", "center", and
"right"` by adding the missing closing quote around left in the paragraph string
(the JSX paragraph in examples/react/column-pinning-split/src/main.tsx where the
splitting APIs are described).
| <input | ||
| type="number" | ||
| min="1" | ||
| max={table.getPageCount()} | ||
| defaultValue={table.state.pagination.pageIndex + 1} | ||
| onChange={(e) => { | ||
| const page = e.target.value ? Number(e.target.value) - 1 : 0 | ||
| table.setPageIndex(page) | ||
| }} |
There was a problem hiding this comment.
Keep the page-jump input in sync with pagination state.
Using defaultValue here makes the field drift out of sync after the user changes pages with the navigation buttons. Bind it to table.state.pagination.pageIndex instead.
💡 Proposed fix
<input
type="number"
min="1"
max={table.getPageCount()}
- defaultValue={table.state.pagination.pageIndex + 1}
+ value={table.state.pagination.pageIndex + 1}
onChange={(e) => {
- const page = e.target.value ? Number(e.target.value) - 1 : 0
- table.setPageIndex(page)
+ const nextValue = e.target.value
+ table.setPageIndex(nextValue ? Number(nextValue) - 1 : 0)
}}
className="page-size-input"
/>📝 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.
| <input | |
| type="number" | |
| min="1" | |
| max={table.getPageCount()} | |
| defaultValue={table.state.pagination.pageIndex + 1} | |
| onChange={(e) => { | |
| const page = e.target.value ? Number(e.target.value) - 1 : 0 | |
| table.setPageIndex(page) | |
| }} | |
| <input | |
| type="number" | |
| min="1" | |
| max={table.getPageCount()} | |
| value={table.state.pagination.pageIndex + 1} | |
| onChange={(e) => { | |
| const nextValue = e.target.value | |
| table.setPageIndex(nextValue ? Number(nextValue) - 1 : 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 `@examples/react/row-selection/src/main.tsx` around lines 242 - 250, The
page-jump input is uncontrolled (uses defaultValue) and drifts from table
pagination state; make it a controlled input by replacing defaultValue with
value bound to (table.state.pagination.pageIndex + 1) and keep the onChange
handler to parse the entered number and call table.setPageIndex(page - 1);
ensure the value is converted to a string for the input and keep min={1} and
max={table.getPageCount()} so the field stays in sync with pagination state
(refer to the input element and
table.state.pagination.pageIndex/table.setPageIndex).
| <input | ||
| type="number" | ||
| min="1" | ||
| max={table.getPageCount()} | ||
| defaultValue={table.state.pagination.pageIndex + 1} | ||
| onChange={(e) => { | ||
| const page = e.target.value ? Number(e.target.value) - 1 : 0 | ||
| table.setPageIndex(page) | ||
| }} |
There was a problem hiding this comment.
Keep the page number input in sync.
defaultValue is only applied on mount, so this field can show a stale page after firstPage/nextPage/page-size changes. Bind it to table.state.pagination.pageIndex + 1 instead.
💡 Suggested change
- defaultValue={table.state.pagination.pageIndex + 1}
+ value={table.state.pagination.pageIndex + 1}🤖 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/with-tanstack-form/src/main.tsx` around lines 331 - 339, The
page number input uses defaultValue so it becomes stale; change the input to be
controlled by binding its value to table.state.pagination.pageIndex + 1 (instead
of defaultValue) and keep the existing onChange which computes const page =
e.target.value ? Number(e.target.value) - 1 : 0 and calls
table.setPageIndex(page); ensure the input still uses min="1" and
max={table.getPageCount()} so table.getPageCount(),
table.state.pagination.pageIndex, and table.setPageIndex are the referenced
symbols to update.
| {#snippet Filter( | ||
| column: Column<typeof _features, Person>, | ||
| table: SvelteTable<typeof _features, Person, any>, | ||
| )} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find other usages of SvelteTable type with three generic parameters
# Search for SvelteTable type usage patterns
rg -nP --type=ts --type=svelte -C3 'SvelteTable<[^>]+,\s*[^>]+,\s*[^>]+>' -g '!node_modules'Repository: TanStack/table
Length of output: 89
🏁 Script executed:
#!/bin/bash
# Read the specific file to see the actual code
cat -n examples/svelte/row-selection/src/App.svelte | sed -n '110,125p'Repository: TanStack/table
Length of output: 531
🏁 Script executed:
#!/bin/bash
# Read lines around 301-304
cat -n examples/svelte/row-selection/src/App.svelte | sed -n '295,310p'Repository: TanStack/table
Length of output: 573
🏁 Script executed:
#!/bin/bash
# Search for SvelteTable type usage without type filter (just search .svelte files directly)
rg -n 'SvelteTable<' examples/svelte/row-selection/src/App.svelte -A2 -B2Repository: TanStack/table
Length of output: 225
🏁 Script executed:
#!/bin/bash
# Find type definitions for SvelteTable in the codebase
fd -e ts -e d.ts | xargs rg -l 'type SvelteTable' | head -5Repository: TanStack/table
Length of output: 106
🏁 Script executed:
#!/bin/bash
# Find and examine the SvelteTable type definition
cat -n packages/svelte-table/src/createTable.svelte.ts | head -100Repository: TanStack/table
Length of output: 3980
🏁 Script executed:
#!/bin/bash
# Search for SvelteTable interface/type definition
rg -n 'export.*SvelteTable' packages/svelte-table/src/ -A10Repository: TanStack/table
Length of output: 2909
🏁 Script executed:
#!/bin/bash
# Check the full selector type and how state is used
rg -n 'createTable' examples/svelte/row-selection/src/App.svelte -B5 -A15 | head -50Repository: TanStack/table
Length of output: 1139
🏁 Script executed:
#!/bin/bash
# Check other examples to see how they handle the third type parameter
find examples/ -name "*.svelte" -type f | xargs rg -l 'SvelteTable<' | head -3Repository: TanStack/table
Length of output: 187
🏁 Script executed:
#!/bin/bash
# Look at the actual selector and table types in the example more carefully
cat -n examples/svelte/row-selection/src/App.svelte | sed -n '40,130p'Repository: TanStack/table
Length of output: 3111
🏁 Script executed:
#!/bin/bash
# Check if there's a type alias for the selected state in this example
rg -n 'type.*RowSelection|type.*SelectedState|interface.*Selected' examples/svelte/row-selection/src/App.svelteRepository: TanStack/table
Length of output: 40
🏁 Script executed:
#!/bin/bash
# Check how the row-pinning example handles SvelteTable typing
rg -n 'SvelteTable<' examples/svelte/row-pinning/src/App.svelte -B3 -A3Repository: TanStack/table
Length of output: 321
🏁 Script executed:
#!/bin/bash
# Check if selector's return type is explicitly inferred somewhere
rg -n 'type.*Selected|TSelected' examples/svelte/row-selection/src/App.svelteRepository: TanStack/table
Length of output: 40
🏁 Script executed:
#!/bin/bash
# Look for how table is actually used to understand if any causes issues
cat -n examples/svelte/row-selection/src/App.svelte | sed -n '130,150p'Repository: TanStack/table
Length of output: 1049
🏁 Script executed:
#!/bin/bash
# Verify that the Filter snippet actually receives a properly typed table and check if removing the explicit any works
# Let's see how createTable infers the type when selector is provided
rg -n 'const table = createTable' examples/svelte/row-selection/src/App.svelte -A2Repository: TanStack/table
Length of output: 119
🏁 Script executed:
#!/bin/bash
# Check TypeScript version and configuration to understand type inference capabilities
cat -n tsconfig.json | head -20Repository: TanStack/table
Length of output: 859
Remove the any type parameter and let TypeScript infer the correct type from the selector.
The third type parameter of SvelteTable should match the shape returned by your selector function (lines 115-119). Using any eliminates type checking and IDE support for table.state properties in the Filter snippet. Since you provide a selector to createTable, TypeScript can automatically infer the correct type—you don't need to specify it explicitly.
{`#snippet` Filter(
column: Column<typeof _features, Person>,
- table: SvelteTable<typeof _features, Person, any>,
+ table: SvelteTable<typeof _features, Person>,
)}Alternatively, explicitly type it as: SvelteTable<typeof _features, Person, { rowSelection: RowSelectionState; globalFilter: string | undefined; pagination: PaginationState }>
🤖 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/row-selection/src/App.svelte` around lines 301 - 304, The
Filter snippet is using SvelteTable<typeof _features, Person, any>, which
disables type inference for table.state; change it to remove the explicit third
type parameter so TypeScript infers the selector return type (i.e., use
SvelteTable<typeof _features, Person> or omit the third param) or explicitly set
the third parameter to the selector shape (e.g., { rowSelection:
RowSelectionState; globalFilter: string | undefined; pagination: PaginationState
}); update the declaration where Filter is used and rely on the selector passed
to createTable to provide correct types for table.state and IDE support.
| Object.defineProperty(table, 'state', { | ||
| value: computed(() => selector(table.store.get())), | ||
| value: computed(() => selector?.(table.store.get()) ?? table.store.get()), | ||
| }) |
There was a problem hiding this comment.
Avoid falling back when the selector returns undefined.
Line 184 currently nullish-coalesces the selector result with table.store.get(), so a legitimate undefined selection gets replaced by the full table state. That breaks the Signal<Readonly<TSelected>> contract for selectors like state => state.rowSelection[rowId]. Branch on whether selector exists instead of coalescing its return value.
💡 Proposed fix
Object.defineProperty(table, 'state', {
- value: computed(() => selector?.(table.store.get()) ?? table.store.get()),
+ value: computed(() => {
+ const state = table.store.get()
+ return selector ? selector(state) : state
+ }),
})📝 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.
| Object.defineProperty(table, 'state', { | |
| value: computed(() => selector(table.store.get())), | |
| value: computed(() => selector?.(table.store.get()) ?? table.store.get()), | |
| }) | |
| Object.defineProperty(table, 'state', { | |
| value: computed(() => { | |
| const state = table.store.get() | |
| return selector ? selector(state) : state | |
| }), | |
| }) |
🤖 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/angular-table/src/injectTable.ts` around lines 183 - 185, The
computed property assigned to table.state uses a nullish coalescing fallback
which replaces legitimate undefined selector results with the full store; change
the logic so you only branch on the existence of selector rather than its return
value—i.e., in the Object.defineProperty for 'state' (the computed(() => ...))
call, call selector(table.store.get()) when selector is present and otherwise
return table.store.get(), so a selector that returns undefined is preserved.
| selector?: (state: TableState<TFeatures>) => TSelected, | ||
| ): SvelteTable<TFeatures, TData, TSelected> { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 1. Check useSelector type signature from `@tanstack/svelte-store`
echo "=== `@tanstack/svelte-store` useSelector signature ==="
fd -e d.ts "index" --search-path node_modules/@tanstack/svelte-store --exec cat {}
echo ""
echo "=== useSelector calls in svelte-table packages ==="
rg -n "useSelector" --type ts packages/svelte-table/
echo ""
echo "=== Optional / missing selector patterns in other adapters ==="
rg -n "selector\?" --type ts packages/react-table/ packages/preact-table/ packages/solid-table/ packages/angular-table/ packages/lit-table/ 2>/dev/null
echo ""
echo "=== How other adapters default the selector ==="
rg -n -A 3 "selector.*=.*state.*=>.*state" --type ts packages/Repository: TanStack/table
Length of output: 5301
🏁 Script executed:
#!/bin/bash
# Check the actual implementation of createTable.svelte.ts around line 91
echo "=== createTable.svelte.ts lines 80-100 ==="
sed -n '80,100p' packages/svelte-table/src/createTable.svelte.ts
echo ""
echo "=== subscribe.ts full content to see optional selector handling ==="
cat packages/svelte-table/src/subscribe.ts
echo ""
echo "=== Check for svelte-store type definition or package.json ==="
cat packages/svelte-store/package.json 2>/dev/null || echo "Not found"Repository: TanStack/table
Length of output: 1624
🏁 Script executed:
#!/bin/bash
# Look for svelte-store source or types to understand useSelector signature
echo "=== Search for useSelector in svelte-store ==="
find packages/svelte-store -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.d.ts" \) -exec grep -l "useSelector" {} \;
echo ""
echo "=== Check dist or src structure in svelte-store ==="
ls -la packages/svelte-store/src/ 2>/dev/null || ls -la packages/svelte-store/dist/ 2>/dev/null || ls -la packages/svelte-store/ 2>/dev/null | head -20Repository: TanStack/table
Length of output: 233
🌐 Web query:
@tanstack/svelte-store useSelector typescript signature optional undefined
💡 Result:
In @tanstack/svelte-store, useSelector’s TypeScript signature is (based on the official docs): function useSelector<TState, TSelected>(source, selector, options): object and the return value is an object with a readonly current: TSelected property [1]. Selector parameter - selector: (state) => TSelected [1] - options: UseSelectorOptions (defaults to {}) [1] About “optional/undefined” - The docs show selector as a required parameter in the useSelector signature (no selector?:... shown) [1]. So TSelected will not automatically become undefined just because you omitted selector. - The return type is current: TSelected (not TSelected | undefined) [1]. - If you are seeing undefined in your code, it’s typically due to your own state possibly being undefined, or because you’re using a different hook (e.g., useStore) where selector is explicitly optional. For comparison, useStore’s selector parameter is documented as optional (selector?:...) and it returns current: TSelected [2]. Relevant type parameters - TSelected defaults to NoInfer in the Svelte docs [1]. If you paste your exact useSelector call, I can point out why your current is coming out undefined and what the correct TSelected annotation should be.
Citations:
- 1: https://tanstack.com/store/latest/docs/framework/svelte/reference/functions/useSelector
- 2: https://mintlify.com/TanStack/store/api/svelte/use-store
🏁 Script executed:
#!/bin/bash
# Search for tests or examples that call createTable without selector
echo "=== Tests calling createTable in svelte-table ==="
find packages/svelte-table -name "*.test.*" -o -name "*.spec.*" | head -10
echo ""
echo "=== Check for __tests__ or test directories ==="
find packages/svelte-table -type d \( -name "__tests__" -o -name "test" -o -name "tests" \)
echo ""
echo "=== Look for .test.ts or .spec.ts files ==="
fd -e test.ts -e spec.ts packages/svelte-table/
echo ""
echo "=== Look for usage examples in README or docs ==="
find packages/svelte-table -name "README*" -o -name "*.md" | xargs grep -l "createTable" 2>/dev/nullRepository: TanStack/table
Length of output: 667
🏁 Script executed:
#!/bin/bash
# Check the installed version and dependencies
echo "=== package.json for svelte-table ==="
cat packages/svelte-table/package.json | grep -A 5 -B 5 "@tanstack/svelte-store"
echo ""
echo "=== Root package.json dependencies ==="
cat package.json | grep -A 20 "dependencies" | grep -i svelte-store
echo ""
echo "=== Look for lockfile entries ==="
grep "@tanstack/svelte-store" pnpm-lock.yaml 2>/dev/null | head -20 || grep "@tanstack/svelte-store" package-lock.json 2>/dev/null | head -20 || echo "No lockfile match"Repository: TanStack/table
Length of output: 934
🏁 Script executed:
#!/bin/bash
# Re-examine subscribe.ts overloads more carefully and check createTable signature
echo "=== subscribe.ts function signatures (lines 1-35) ==="
head -35 packages/svelte-table/src/subscribe.ts
echo ""
echo "=== createTable.svelte.ts function signature and selector parameter ==="
head -50 packages/svelte-table/src/createTable.svelte.ts | tail -30Repository: TanStack/table
Length of output: 2173
🏁 Script executed:
#!/bin/bash
# Search for any imports or references to understand how svelte-store's useSelector actually works
echo "=== Check if there's any type annotation or overload documentation ==="
grep -r "useSelector" packages/svelte-table/src/ -A 2 -B 2
echo ""
echo "=== Search the entire repo for how useSelector is used elsewhere ==="
rg "useSelector\(" --type ts -A 2 -B 1 | head -40
echo ""
echo "=== Check createTable integration tests or examples ==="
find . -name "*.test.*" -o -name "*.spec.*" | xargs grep -l "createTable" 2>/dev/null | head -5Repository: TanStack/table
Length of output: 5021
🌐 Web query:
@tanstack/svelte-store 0.12.0 useSelector undefined selector implementation
💡 Result:
In @tanstack/svelte-store, “useSelector” does exist and it does not take a missing/undefined “selector” argument—you pass a selector function as the 2nd argument, and you read the selected value from the returned object’s .current. The API shape is: - useSelector(source, selector, options) - selector: (state) => TSelected - return: object with readonly current: TSelected [1] Example usage from the official docs: - const count = useSelector(counterStore, (state) => state.count) and then count.current [1] So if you’re seeing “useSelector undefined” or “selector undefined”, the most likely issue is that your call site is wrong (e.g., you’re calling a different hook like useStore/useAtom, importing from the wrong package/version, or calling useSelector with only one argument so the required selector parameter is missing). The docs also show useStore(store, selector?) where selector is optional, but useSelector(source, selector, ...) is described with selector present in the signature and return type uses .current [2].
Citations:
- 1: https://tanstack.com/store/latest/docs/framework/svelte/reference/functions/useSelector
- 2: https://mintlify.com/TanStack/store/frameworks/svelte
Add defensive guard for optional selector before calling useSelector
selector is optional but line 91 passes it directly to useSelector(table.store, selector) without a fallback. Other adapters (vue-table, solid-table) explicitly guard the selector with conditional logic before invoking useSelector:
const selected = props.selector
? useSelector(table.store, props.selector)
: undefinedSvelte-table should follow the same defensive pattern to ensure table.state is properly initialized even when no selector is provided:
Suggested fix
- const stateStore = useSelector(table.store, selector)
+ const stateStore = useSelector(
+ table.store,
+ selector ?? ((state) => state as unknown as TSelected),
+ )🤖 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/svelte-table/src/createTable.svelte.ts` around lines 36 - 37, The
optional selector parameter should be guarded before calling useSelector: change
the code around the creation of selected in createTable (where
useSelector(table.store, selector) is called) to only invoke useSelector when
selector is truthy and otherwise set selected to undefined (matching the pattern
used in other adapters), ensuring table.state/state initialization isn't
dependent on a missing selector; locate the useSelector(table.store, selector)
call and replace it with a conditional that checks selector first and assigns
selected accordingly.
🎯 Changes
✅ Checklist
pnpm test:pr.Summary by CodeRabbit
New Features
Improvements
Documentation