Skip to content

feat: refactor reactivity bindings to work without tanstack-store#6289

Merged
KevinVandy merged 6 commits into
alphafrom
fix/native-reactivity-with-external-atoms
Jun 3, 2026
Merged

feat: refactor reactivity bindings to work without tanstack-store#6289
KevinVandy merged 6 commits into
alphafrom
fix/native-reactivity-with-external-atoms

Conversation

@riccardoperra
Copy link
Copy Markdown
Collaborator

@riccardoperra riccardoperra commented Jun 2, 2026

Summary by CodeRabbit

  • New Features

    • Angular devtools integration for tables; many examples and demos now display the full table.state.
    • "Shuffle Columns" control added to several demo UIs.
  • Bug Fixes

    • Improved reactivity lifecycle and cleanup across adapters to ensure proper teardown.
    • Safer handling and two-way synchronization for external atoms to avoid feedback loops.
  • Docs/UX

    • Clear error messages when certain adapter lifecycle/hooks are unsupported.

@riccardoperra riccardoperra marked this pull request as draft June 2, 2026 09:54
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jun 2, 2026

View your CI Pipeline Execution ↗ for commit 1a21f38

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

☁️ Nx Cloud last updated this comment at 2026-06-03 21:58:44 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 2, 2026

More templates

@tanstack/angular-table

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

@tanstack/angular-table-devtools

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

@tanstack/lit-table

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

@tanstack/match-sorter-utils

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

@tanstack/preact-table

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

@tanstack/preact-table-devtools

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

@tanstack/react-table

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

@tanstack/react-table-devtools

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

@tanstack/solid-table

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

@tanstack/solid-table-devtools

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

@tanstack/svelte-table

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

@tanstack/table-core

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

@tanstack/table-devtools

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

@tanstack/vue-table

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

@tanstack/vue-table-devtools

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

commit: c706280

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1159f60-c26b-4fa4-a116-62e5d61c2172

📥 Commits

Reviewing files that changed from the base of the PR and between 1a21f38 and c706280.

📒 Files selected for processing (2)
  • packages/vue-table/src/merge-proxy.ts
  • packages/vue-table/src/useTable.ts

📝 Walkthrough

Walkthrough

Adds external-atom wrapping and subscription lifecycle hooks to reactivity bindings; implements wrapping and guarded two-way sync in constructTable; refactors Angular and Solid adapters to wrap framework signals directly with subscription/unmount support; other adapters opt out; multiple examples now render full table.state for debugging.

Changes

External Atom Wrapping and Adapter Lifecycle Management

Layer / File(s) Summary
Reactivity interface expansion
packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts
Adds wrapExternalAtoms: boolean, addSubscription(subscription: Subscription), and optional unmount?: () => void to TableReactivityBindings.
Core external-atom wrapping
packages/table-core/src/core/table/constructTable.ts
When _reactivity.wrapExternalAtoms and mergedOptions.atoms exist, replaces external atoms with writable wrapped atoms, sets up guarded two-way sync, registers subscriptions via _reactivity.addSubscription, and only creates baseAtoms if no external atom is provided.
Angular adapter: Signal-based atom wrappers
packages/angular-table/src/reactivity.ts
Implements signalToReadonlyAtom/signalToWritableAtom using computed, toObservable, and untracked; reimplements createReadonlyAtom/createWritableAtom with Angular computed/signal; adds subscription tracking and unmount.
Angular table lifecycle wiring
packages/angular-table/src/injectTable.ts
Adds DestroyRef.onDestroy registration to call table._reactivity.unmount?.() during injector teardown and reorganizes type imports.
Solid adapter: Signal-based atom wrappers
packages/solid-table/src/reactivity.ts
Refactors helpers to wrap Solid accessors/signals directly; uses createMemo for readonly atoms and createSignal for writable atoms; adds addSubscription and centralized unmount.
Solid table lifecycle wiring
packages/solid-table/src/createTable.ts
Creates a single reactivity via solidReactivity(owner), wires it into table options, and registers onCleanup to call reactivity.unmount?.().
Vue: merge-proxy -> flatMerge and useTable changes
packages/vue-table/src/merge-proxy.ts, packages/vue-table/src/useTable.ts
Harden resolveSource, add exported flatMerge that eagerly resolves sources and skips undefined values, and switch useTable to use flatMerge for syncing/merging reactive options.
Other adapters: opt-out stubs
packages/react-table/src/reactivity.ts, packages/vue-table/src/reactivity.ts, packages/lit-table/src/reactivity.ts, packages/preact-table/src/reactivity.ts, packages/svelte-table/src/reactivity.svelte.ts, packages/table-core/src/store-reactivity-bindings.ts
Set wrapExternalAtoms: false and add addSubscription/unmount stubs that throw "Feature not supported in current reactivity implementation".
Preserve static options across setOptions
packages/table-core/src/core/table/coreTablesFeature.utils.ts
table_setOptions re-applies static initialization-only fields (_rowModels, _features, atoms, initialState) onto the merged options result to preserve those references.
Examples: show full table.state in debug UIs and small UI tweaks
examples/* (Angular, React, Preact, Solid, Svelte, Vue, Lit)
Replace various example debug <pre> outputs to render JSON.stringify(table.state, null, 2); add minor example UI controls (e.g., Shuffle Columns), simplified button markup, and small demo tweaks.

Estimated code review effort:
🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs:

Suggested reviewers:

  • schiller-manuel
  • KevinVandy

"I'm a rabbit, I watched atoms bind,
Signals hop and sync in kind,
Cleanup called when scopes unwind,
Tables hum with state aligned,
Hooray — devtools show the mind!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: refactor reactivity bindings to work without tanstack-store' accurately and clearly summarizes the main change: refactoring the internal reactivity system to eliminate TanStack Store dependency.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 fix/native-reactivity-with-external-atoms

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

❤️ Share

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

nx-cloud[bot]

This comment was marked as outdated.

@riccardoperra riccardoperra force-pushed the fix/native-reactivity-with-external-atoms branch from fb969ef to ba8f961 Compare June 2, 2026 10:03
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: 2

Caution

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

⚠️ Outside diff range comments (1)
packages/angular-table/src/reactivity.ts (1)

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

Outdated JSDoc comment.

The documentation states "Table state atoms are backed by TanStack Store atoms" but the implementation now uses Angular signals directly via computed/signal and toObservable.

📝 Suggested fix
 /**
  * Creates the table-core reactivity bindings used by the Angular adapter.
  *
- * Table state atoms are backed by TanStack Store atoms. The options store stays
- * framework-native because row-model APIs read `table.options` directly during
- * render. Readonly table atoms bridge Store dependency tracking into Angular
- * computed signals.
+ * Table state atoms are backed by Angular signals. The options store stays
+ * framework-native because row-model APIs read `table.options` directly during
+ * render. Readonly table atoms use `toObservable` to bridge Angular signal
+ * updates into atom subscription semantics.
  */
🤖 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/reactivity.ts` around lines 62 - 68, Update the
outdated JSDoc block that begins "Creates the table-core reactivity bindings
used by the Angular adapter." to accurately describe the current implementation:
state atoms are implemented with Angular signals via computed/signal and interop
uses toObservable (not TanStack Store atoms), mention that options remain
framework-native and readonly atoms bridge Angular computed signals where
applicable; update any terminology and examples in that comment to reference
computed, signal, and toObservable so readers understand the actual mechanism
used.
🧹 Nitpick comments (1)
packages/solid-table/src/reactivity.ts (1)

23-33: 💤 Low value

Clarify owner-context risk: atom creation is synchronous during createTable, so the original concern mainly applies to out-of-contract async usage.
createReadonlyAtom/createWritableAtom in packages/solid-table/src/reactivity.ts call createMemo/createSignal without runWithOwner(owner, ...), but table atom creation happens synchronously inside createTable/constructTable after createTable captures owner via getOwner()!. The owner mismatch risk would only apply if someone uses solidReactivity(owner) to create atoms later from a different owner context (not the normal table flow).

🤖 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/solid-table/src/reactivity.ts` around lines 23 - 33, Update the
inline comment/docs in packages/solid-table/src/reactivity.ts to clarify the
owner-context risk: state that createReadonlyAtom/createWritableAtom (and
signalToReadonlyAtom) are invoked synchronously inside
createTable/constructTable after createTable captures the owner via getOwner(),
so owner mismatch is only a concern when consumers call solidReactivity(owner)
to create atoms later from a different owner (i.e., out-of-contract async
usage); reference the functions signalToReadonlyAtom, createReadonlyAtom,
createWritableAtom and the getOwner() capture to make the scope and safe
synchronous assumption explicit.
🤖 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 `@packages/lit-table/src/reactivity.ts`:
- Around line 22-26: The current export in packages/lit-table/src/reactivity.ts
defines unmount as a function that throws, which can break callers expecting an
optional no-op; update the reactivity binding so unmount is omitted or
implemented as a harmless no-op instead of throwing (i.e., remove the throwing
implementation for unmount in the exported object returned from the Lit
reactivity adapter), leaving addSubscription unchanged; this keeps
TableController.hostDisconnected and constructTable (with wrapExternalAtoms
usage) safe while preserving the optional unmount contract.

In `@packages/solid-table/src/createTable.ts`:
- Around line 127-132: The merge currently allows user-supplied
coreReativityFeature to override the adapter's binding because in mergedOptions
you spread ...tableOptions._features after coreReativityFeature; change the
merge so the adapter's coreReativityFeature wins by placing
...tableOptions._features before coreReativityFeature (i.e., ensure the object
passed to mergeProps sets coreReativityFeature: reactivity last), updating the
creation of mergedOptions (in createTable.ts where mergeProps, tableOptions,
_features, coreReativityFeature, and reactivity are referenced) accordingly.

---

Outside diff comments:
In `@packages/angular-table/src/reactivity.ts`:
- Around line 62-68: Update the outdated JSDoc block that begins "Creates the
table-core reactivity bindings used by the Angular adapter." to accurately
describe the current implementation: state atoms are implemented with Angular
signals via computed/signal and interop uses toObservable (not TanStack Store
atoms), mention that options remain framework-native and readonly atoms bridge
Angular computed signals where applicable; update any terminology and examples
in that comment to reference computed, signal, and toObservable so readers
understand the actual mechanism used.

---

Nitpick comments:
In `@packages/solid-table/src/reactivity.ts`:
- Around line 23-33: Update the inline comment/docs in
packages/solid-table/src/reactivity.ts to clarify the owner-context risk: state
that createReadonlyAtom/createWritableAtom (and signalToReadonlyAtom) are
invoked synchronously inside createTable/constructTable after createTable
captures the owner via getOwner(), so owner mismatch is only a concern when
consumers call solidReactivity(owner) to create atoms later from a different
owner (i.e., out-of-contract async usage); reference the functions
signalToReadonlyAtom, createReadonlyAtom, createWritableAtom and the getOwner()
capture to make the scope and safe synchronous assumption explicit.
🪄 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: 4e054110-b6d4-4e15-b737-b3e3f97d111f

📥 Commits

Reviewing files that changed from the base of the PR and between 226e464 and fb969ef.

📒 Files selected for processing (12)
  • examples/angular/basic-external-atoms/src/app/app.ts
  • packages/angular-table/src/injectTable.ts
  • packages/angular-table/src/reactivity.ts
  • packages/lit-table/src/reactivity.ts
  • packages/preact-table/src/reactivity.ts
  • packages/react-table/src/reactivity.ts
  • packages/solid-table/src/createTable.ts
  • packages/solid-table/src/reactivity.ts
  • packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts
  • packages/table-core/src/core/table/constructTable.ts
  • packages/table-core/src/store-reactivity-bindings.ts
  • packages/vue-table/src/reactivity.ts
💤 Files with no reviewable changes (1)
  • examples/angular/basic-external-atoms/src/app/app.ts

Comment on lines +22 to +26
unmount: () => {
throw new Error(
'Feature not supported in current reactivity implementation',
)
},
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
# Find all call sites of reactivity.unmount and how presence is checked
rg -nP '\bunmount\b' --type=ts -C3 -g '!**/*.test.ts' packages

Repository: TanStack/table

Length of output: 8030


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find how lit-table calls the adapter unmount during teardown
rg -nP '\bunmount\s*[\?\)]' packages/lit-table -S

# Also check whether lit uses the shared core reactivity binding (table._reactivity.unmount?.)
rg -nP 'table\._reactivity\.unmount\?\.' packages/lit-table -S || true

# Check any generic/framework-agnostic unmount invocation that lit's adapter might be wired into
rg -nP '\.unmount\?\.' packages/table-core -S
rg -nP '\._reactivity\.unmount' packages -S

Repository: TanStack/table

Length of output: 40


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "== Call sites: table._reactivity.unmount?.() =="
rg -nP 'table\._reactivity\.unmount\?\s*\(' --type=ts packages -S -g '!**/*.test.ts' || echo "(none)"

echo "== Call sites: reactivity.unmount?.() =="
rg -nP 'reactivity\.unmount\?\s*\(' --type=ts packages -S -g '!**/*.test.ts' || echo "(none)"

echo "== Call sites: reactivity.unmount (direct) =="
rg -nP 'reactivity\.unmount\s*\(' --type=ts packages -S -g '!**/*.test.ts' || echo "(none)"

echo "== Call sites: .unmount?.() (generic) =="
rg -nP '\.unmount\?\s*\(' --type=ts packages -S -g '!**/*.test.ts' || echo "(none)"

echo "== Call sites: _reactivity.unmount (any form) =="
rg -nP '\._reactivity\.unmount\b' --type=ts packages -S -g '!**/*.test.ts' || echo "(none)"

Repository: TanStack/table

Length of output: 395


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "== Call sites: table._reactivity.unmount =="
rg -nP 'table\._reactivity\.unmount\b' --type=ts packages -S -g '!**/*.test.ts' || echo "(none)"

echo "== Call sites: ._reactivity.unmount( =="
rg -nP '\._reactivity\.unmount\s*\(' --type=ts packages -S -g '!**/*.test.ts' || echo "(none)"

echo "== Call sites: core.unmount =="
rg -nP '\.unmount\s*\(' --type=ts packages -S -g '!**/*.test.ts' packages/react-table packages/vue-table packages/preact-table packages/lit-table || echo "(none)"

echo "== Call sites: reactivity.unmount =="
rg -nP '\breactivity\.unmount\b' --type=ts packages -S -g '!**/*.test.ts' || echo "(none)"

Repository: TanStack/table

Length of output: 643


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "== table-core: any access to _reactivity.unmount / reactivity.unmount =="
rg -nP '\.(_reactivity|reactivity)\.unmount\b' packages/table-core -S || echo "(none)"

echo "== table-core: any call to unmount() (direct) =="
rg -nP '\bunmount\s*\(\s*\)' packages/table-core -S || echo "(none)"

echo "== table-core: any call to unmount?.() (optional) =="
rg -nP '\bunmount\s*\?\.' packages/table-core -S || echo "(none)"

echo "== table-core: core reactivity bindings destroy/unmount wiring =="
rg -nP 'store-reactivity-bindings|coreReactivityFeature|reactivityFeature|_reactivity' packages/table-core -S

Repository: TanStack/table

Length of output: 5042


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "== table-core: all unmount mentions (non-tests) =="
rg -nP '\bunmount\b' packages/table-core/src -S | head -n 200

echo "== table-core: any call to reactivity.unmount or _reactivity.unmount (broader) =="
rg -nP '\b(reactivity|_reactivity)\.unmount\b' packages/table-core/src -S || echo "(none)"

echo "== table-core: any call to unmount() on something called core / table =="
rg -nP '\.unmount\s*\(\s*\)' packages/table-core/src -S || echo "(none)"

echo "== lit-table: any reference to _reactivity.unmount / reactivity.unmount / core.unmount =="
rg -nP '\b(_reactivity|reactivity)\.unmount\b|\.unmount\s*\(' packages/lit-table/src -S || echo "(none)"

echo "== react-table: any reference to _reactivity.unmount / reactivity.unmount / core.unmount =="
rg -nP '\b(_reactivity|reactivity)\.unmount\b|\.unmount\s*\(' packages/react-table/src -S || echo "(none)"

echo "== preact-table: any reference to _reactivity.unmount / reactivity.unmount / core.unmount =="
rg -nP '\b(_reactivity|reactivity)\.unmount\b|\.unmount\s*\(' packages/preact-table/src -S || echo "(none)"

echo "== vue-table: any reference to _reactivity.unmount / reactivity.unmount / core.unmount =="
rg -nP '\b(_reactivity|reactivity)\.unmount\b|\.unmount\s*\(' packages/vue-table/src -S || echo "(none)"

Repository: TanStack/table

Length of output: 845


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the contents around the unmount stub in table-core
sed -n '1,120p' packages/table-core/src/store-reactivity-bindings.ts

# Find and inspect where core "destroy"/"unmount" lifecycle is implemented (coreTablesFeature etc.)
echo "---- coreTablesFeature for destroy/unmount ----"
rg -nP 'destroy\(|unmount\(|teardown|cleanup' packages/table-core/src/core -S

# Grab key files that likely implement table.destroy / lifecycle
ls packages/table-core/src/core/table 2>/dev/null || true
ls packages/table-core/src/core 2>/dev/null || true

# Inspect constructTable.ts where reactivity bindings are wired; then search within for unmount/destroy usage
sed -n '1,220p' packages/table-core/src/core/table/constructTable.ts
rg -nP 'destroy\(|unmount|cleanup' packages/table-core/src/core/table/constructTable.ts -S || true

Repository: TanStack/table

Length of output: 7411


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== lit-table: unmount invocations =="
rg -nP '\.(_reactivity\.)?unmount\s*\?\.' packages/lit-table/src -S || echo "(none)"
rg -nP '\.(_reactivity\.)?unmount\s*\(' packages/lit-table/src -S || echo "(none)"
rg -nP '\bunmount\b' packages/lit-table/src -S || echo "(none)"

echo "== lit-table: likely teardown hooks =="
rg -nP '(disconnectedCallback|disconnected|disposal|cleanup|willDestroy|dispose|onDisconnect|disconnectedCallback)' packages/lit-table/src -S || echo "(none)"

echo "== lit-table: where reactivity binding object is stored =="
rg -nP 'reactivity|_reactivity' packages/lit-table/src -S

Repository: TanStack/table

Length of output: 1396


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -nS '(destroy|dispose|cleanup|disconnectedCallback|disconnected|onDisconnect|willDestroy|unmount)' packages/lit-table/src || echo "(none)"

echo "---- TableController.ts (relevant excerpts around lifecycle/cleanup) ----"
# Show portions where controllers are constructed and where any cleanup might be defined
sed -n '1,260p' packages/lit-table/src/TableController.ts

echo "---- lit-table/src/reactivity.ts ----"
sed -n '1,120p' packages/lit-table/src/reactivity.ts

Repository: TanStack/table

Length of output: 9366


Avoid throwing unmount in Lit reactivity bindings

packages/lit-table/src/reactivity.ts sets unmount: () => { throw ... }. Lit’s teardown (TableController.hostDisconnected) only unsubscribes subscriptions and table-core doesn’t call reactivity.unmount, so this won’t crash in current Lit teardown. Still, unmount is optional (unmount?: () => void); if any teardown path later calls it with ?.() the throw defeats the intended no-op contract—prefer omitting unmount (or making it a no-op). addSubscription is fine to throw because constructTable only reaches it behind wrapExternalAtoms, which Lit sets to false.

♻️ Proposed change
     addSubscription: () => {
       throw new Error(
         'Feature not supported in current reactivity implementation',
       )
     },
-    unmount: () => {
-      throw new Error(
-        'Feature not supported in current reactivity implementation',
-      )
-    },
📝 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
unmount: () => {
throw new Error(
'Feature not supported in current reactivity implementation',
)
},
🤖 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/lit-table/src/reactivity.ts` around lines 22 - 26, The current
export in packages/lit-table/src/reactivity.ts defines unmount as a function
that throws, which can break callers expecting an optional no-op; update the
reactivity binding so unmount is omitted or implemented as a harmless no-op
instead of throwing (i.e., remove the throwing implementation for unmount in the
exported object returned from the Lit reactivity adapter), leaving
addSubscription unchanged; this keeps TableController.hostDisconnected and
constructTable (with wrapExternalAtoms usage) safe while preserving the optional
unmount contract.

Comment on lines 127 to 132
const mergedOptions = mergeProps(tableOptions, {
_features: {
coreReativityFeature: solidReactivity(owner),
coreReativityFeature: reactivity,
...tableOptions._features,
},
}) as any
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

Merge order allows user to override adapter reactivity.

The spread order places ...tableOptions._features after coreReativityFeature: reactivity, which means if a user passes their own coreReativityFeature in tableOptions._features, it will silently override the Solid adapter's bindings. This is likely unintentional since the adapter should enforce its framework-specific reactivity.

Consider reversing the order so the adapter's reactivity wins:

Proposed fix
   const mergedOptions = mergeProps(tableOptions, {
     _features: {
-      coreReativityFeature: reactivity,
       ...tableOptions._features,
+      coreReativityFeature: reactivity,
     },
   }) as any
🤖 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/solid-table/src/createTable.ts` around lines 127 - 132, The merge
currently allows user-supplied coreReativityFeature to override the adapter's
binding because in mergedOptions you spread ...tableOptions._features after
coreReativityFeature; change the merge so the adapter's coreReativityFeature
wins by placing ...tableOptions._features before coreReativityFeature (i.e.,
ensure the object passed to mergeProps sets coreReativityFeature: reactivity
last), updating the creation of mergedOptions (in createTable.ts where
mergeProps, tableOptions, _features, coreReativityFeature, and reactivity are
referenced) accordingly.

@riccardoperra riccardoperra force-pushed the fix/native-reactivity-with-external-atoms branch from ba8f961 to 68b124e Compare June 2, 2026 10:09
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 add the two properties required by the updated TableReactivityBindings interface (wrapExternalAtoms and addSubscription) to the svelte adapter's svelteReactivity() function, which were introduced in @tanstack/table-core by this PR but not applied to the Svelte package. The fix follows the same subscription-tracking pattern used by the Angular and Solid adapters, resolving the svelte-check type error.

Tip

We verified this fix by re-running @tanstack/svelte-table:test:types.

Suggested Fix changes
diff --git a/packages/svelte-table/src/reactivity.svelte.ts b/packages/svelte-table/src/reactivity.svelte.ts
index 8549be44..3078f961 100644
--- a/packages/svelte-table/src/reactivity.svelte.ts
+++ b/packages/svelte-table/src/reactivity.svelte.ts
@@ -4,7 +4,12 @@ import type {
   TableAtomOptions,
   TableReactivityBindings,
 } from '@tanstack/table-core/reactivity'
-import type { Atom, Observer, ReadonlyAtom } from '@tanstack/svelte-store'
+import type {
+  Atom,
+  Observer,
+  ReadonlyAtom,
+  Subscription,
+} from '@tanstack/svelte-store'
 
 const optionsStoreDebugName = 'table/optionsStore'
 
@@ -56,8 +61,18 @@ function createRuneWritableAtom<T>(initialValue: T): Atom<T> {
  * render. Readonly table atoms bridge Store dependency tracking into `$derived.by`.
  */
 export function svelteReactivity(): TableReactivityBindings {
+  const subscriptions = new Set<Subscription>()
+
   return {
     createOptionsStore: true,
+    wrapExternalAtoms: true,
+    addSubscription: (subscription) => {
+      subscriptions.add(subscription)
+    },
+    unmount: () => {
+      subscriptions.forEach((s) => s.unsubscribe())
+      subscriptions.clear()
+    },
     schedule: (fn) => queueMicrotask(() => fn()),
     createReadonlyAtom: <T>(fn: () => T, _options?: TableAtomOptions<T>) => {
       const storeAtom = createAtom(() => fn(), {

Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally PRI9-SMyR

Apply fix locally with your editor ↗   View interactive diff ↗



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

@riccardoperra riccardoperra force-pushed the fix/native-reactivity-with-external-atoms branch from 5270a9d to 3437f18 Compare June 3, 2026 17:09
@riccardoperra riccardoperra force-pushed the fix/native-reactivity-with-external-atoms branch from 2cee21e to 62c3998 Compare June 3, 2026 17:33
@KevinVandy KevinVandy marked this pull request as ready for review June 3, 2026 20:59
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: 2

🤖 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/solid/row-pinning/src/App.tsx`:
- Line 327: The debug output currently renders JSON via
JSON.stringify(table.state(), null, 2) inside a <div> which collapses
whitespace; change the wrapper to a <pre> so newlines and indentation are
preserved for readable state inspection (update the JSX in App.tsx where the
line uses JSON.stringify(table.state(), null, 2) to render inside <pre> instead
of <div>).

In `@examples/svelte/row-pinning/src/App.svelte`:
- Line 385: Replace the debug output div with a preformatted block so the
pretty-printed JSON remains readable; locate the line rendering JSON of
table.state in App.svelte (the expression JSON.stringify(table.state, null, 2))
and render it inside a <pre> (optionally <pre><code>...</code></pre>) instead of
a <div> so whitespace and newlines are preserved.
🪄 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: 8b8a4ce1-c351-4416-bb8f-b8efd946f9c6

📥 Commits

Reviewing files that changed from the base of the PR and between fb969ef and 62c3998.

📒 Files selected for processing (52)
  • examples/angular/basic-external-atoms/src/app/app.ts
  • examples/angular/column-resizing-performant/src/app/app.html
  • examples/angular/column-resizing-performant/src/app/app.ts
  • examples/lit/basic-external-atoms/src/main.ts
  • examples/lit/basic-external-state/src/main.ts
  • examples/lit/expanding/src/main.ts
  • examples/lit/filters/src/main.ts
  • examples/lit/row-pinning/src/main.ts
  • examples/lit/sorting-dynamic-data/src/main.ts
  • examples/lit/sorting/src/main.ts
  • examples/preact/basic-external-atoms/src/main.tsx
  • examples/preact/basic-external-state/src/main.tsx
  • examples/preact/row-pinning/src/main.tsx
  • examples/preact/with-tanstack-query/src/main.tsx
  • examples/react/basic-external-atoms/src/main.tsx
  • examples/react/basic-external-state/src/main.tsx
  • examples/react/row-pinning/src/main.tsx
  • examples/react/with-tanstack-query/src/main.tsx
  • examples/solid/basic-external-atoms/src/App.tsx
  • examples/solid/basic-external-state/src/App.tsx
  • examples/solid/row-pinning/src/App.tsx
  • examples/solid/with-tanstack-query/src/App.tsx
  • examples/svelte/basic-external-atoms/src/App.svelte
  • examples/svelte/basic-external-state/src/App.svelte
  • examples/svelte/column-ordering/src/App.svelte
  • examples/svelte/column-pinning-split/src/App.svelte
  • examples/svelte/column-pinning-sticky/src/App.svelte
  • examples/svelte/column-pinning/src/App.svelte
  • examples/svelte/column-visibility/src/App.svelte
  • examples/svelte/expanding/src/App.svelte
  • examples/svelte/filters-fuzzy/src/App.svelte
  • examples/svelte/grouping/src/App.svelte
  • examples/svelte/pagination/src/App.svelte
  • examples/svelte/row-pinning/src/App.svelte
  • examples/svelte/row-selection/src/App.svelte
  • examples/svelte/sorting/src/App.svelte
  • examples/vue/basic-external-atoms/src/App.tsx
  • examples/vue/basic-external-state/src/App.tsx
  • examples/vue/with-tanstack-query/src/App.tsx
  • packages/angular-table/src/injectTable.ts
  • packages/angular-table/src/reactivity.ts
  • packages/lit-table/src/reactivity.ts
  • packages/preact-table/src/reactivity.ts
  • packages/react-table/src/reactivity.ts
  • packages/solid-table/src/createTable.ts
  • packages/solid-table/src/reactivity.ts
  • packages/svelte-table/src/reactivity.svelte.ts
  • packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts
  • packages/table-core/src/core/table/constructTable.ts
  • packages/table-core/src/core/table/coreTablesFeature.utils.ts
  • packages/table-core/src/store-reactivity-bindings.ts
  • packages/vue-table/src/reactivity.ts
💤 Files with no reviewable changes (10)
  • examples/svelte/expanding/src/App.svelte
  • examples/angular/basic-external-atoms/src/app/app.ts
  • examples/svelte/filters-fuzzy/src/App.svelte
  • examples/svelte/grouping/src/App.svelte
  • examples/svelte/pagination/src/App.svelte
  • examples/lit/expanding/src/main.ts
  • examples/svelte/column-pinning-sticky/src/App.svelte
  • examples/svelte/sorting/src/App.svelte
  • examples/svelte/row-selection/src/App.svelte
  • examples/svelte/column-pinning-split/src/App.svelte
✅ Files skipped from review due to trivial changes (3)
  • examples/lit/filters/src/main.ts
  • examples/angular/column-resizing-performant/src/app/app.html
  • examples/svelte/basic-external-state/src/App.svelte
🚧 Files skipped from review as they are similar to previous changes (11)
  • packages/vue-table/src/reactivity.ts
  • packages/react-table/src/reactivity.ts
  • packages/table-core/src/store-reactivity-bindings.ts
  • packages/preact-table/src/reactivity.ts
  • packages/table-core/src/core/table/constructTable.ts
  • packages/angular-table/src/injectTable.ts
  • packages/solid-table/src/reactivity.ts
  • packages/angular-table/src/reactivity.ts
  • packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts
  • packages/lit-table/src/reactivity.ts
  • packages/solid-table/src/createTable.ts

Comment thread examples/solid/row-pinning/src/App.tsx Outdated
Comment thread examples/svelte/row-pinning/src/App.svelte Outdated
@KevinVandy KevinVandy merged commit f478594 into alpha Jun 3, 2026
8 checks passed
@KevinVandy KevinVandy deleted the fix/native-reactivity-with-external-atoms branch June 3, 2026 22:12
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.

2 participants