Skip to content

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

Draft
riccardoperra wants to merge 1 commit into
alphafrom
fix/native-reactivity-with-external-atoms
Draft

feat: refactor reactivity bindings to work without tanstack-store#6289
riccardoperra wants to merge 1 commit 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

    • Added TanStack Table devtools integration for Angular adapter
    • Enhanced reactivity system with improved lifecycle management and cleanup support across all framework adapters
    • Explicit error messaging for unsupported reactivity features in Lit, Preact, React, and Vue adapters
  • Bug Fixes

    • Improved table destruction and resource cleanup in Angular and Solid adapters

@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 fb969ef

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

☁️ Nx Cloud last updated this comment at 2026-06-02 10:11:59 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: 68b124e

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 880cd7ce-01da-4b90-995e-93f60945be26

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces external atom wrapping and lifecycle management for TanStack Table's reactivity system. The core interface expands to support wrapping external atoms and lifecycle hooks, core table logic implements two-way atom synchronization, Angular and Solid adapters are refactored to use Signal-based wrapping instead of Store atoms, and other adapters provide stub implementations.

Changes

External Atom Wrapping and Adapter Lifecycle Management

Layer / File(s) Summary
Reactivity Interface Expansion
packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts
TableReactivityBindings adds wrapExternalAtoms: boolean, addSubscription(subscription), and optional unmount() to define adapter capabilities for external atom wrapping and lifecycle management.
Core Table External Atom Wrapping
packages/table-core/src/core/table/constructTable.ts
Table constructor wraps user-provided external atoms with writable base atoms, synchronizes values bidirectionally with guard flags to prevent loops, and registers subscriptions when _reactivity.wrapExternalAtoms is enabled.
Angular Adapter Signal-Based Reactivity
packages/angular-table/src/reactivity.ts
Replaces Store-based atoms with signalToReadonlyAtom and signalToWritableAtom wrappers that bridge Angular Signals to atoms via toObservable and computed, replaces createAtom machinery with computed and signal implementations, adds subscription tracking and unmount lifecycle.
Angular Table Lifecycle Wiring
packages/angular-table/src/injectTable.ts
Adds DestroyRef cleanup handler to call table._reactivity.unmount?.() during table teardown.
Solid Adapter Signal-Based Reactivity
packages/solid-table/src/reactivity.ts
Refactors atom helpers to wrap Solid accessors and signals directly, replaces Store atom approach with createMemo for readonly atoms and createSignal for writable atoms, adds subscription tracking and unmount cleanup.
Solid Table Lifecycle Wiring
packages/solid-table/src/createTable.ts
Stores solidReactivity(owner) instance once and registers onCleanup handler to call reactivity.unmount?.() during owner cleanup.
Other Adapter Stub Implementations
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/table-core/src/store-reactivity-bindings.ts
React, Vue, Lit, Preact, and Store adapters implement new interface by setting wrapExternalAtoms: false and stubbing addSubscription and unmount to throw "Feature not supported" errors.
Example Devtools Integration
examples/angular/basic-external-atoms/src/app/app.ts
Removes debug effect logging and wires TanStack Table devtools via injectTanStackTableDevtools.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 Atoms wrap in signals bright,
Lifecycles spring and cleanup tight,
Adapters heed the new refrain,
Signal bridges flow like rain!
External atoms find their way,
The table lives another day! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% 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 accurately describes the main refactoring objective—moving reactivity bindings away from TanStack Store dependency to native framework implementations.
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 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.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant