query icon indicating copy to clipboard operation
query copied to clipboard

fix: support async Svelte

Open dummdidumm opened this issue 1 month ago โ€ข 6 comments

๐ŸŽฏ Changes

Svelte 5 now supports async await in components. One behavior there is that when a component is created as part of a boundary that has pending async work, its $effects will not run until that async work is done.

The way the code is written right now means you can end up in an infinite pending state: If you do await someQuery.promise, the $effect for the subscription will never run, and therefore the await will never resolve. This PR fixes it.

โœ… Checklist

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

๐Ÿš€ Release Impact

  • [x] This change affects published code, and I have generated a changeset.
  • [ ] This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes
    • Restored reliable support for async Svelte so queries behave correctly in modern Svelte environments.
    • Improved query lifecycle and subscription handling to ensure consistent result updates and proper cleanup across server and client lifecycles.

dummdidumm avatar Oct 25 '25 21:10 dummdidumm

๐Ÿฆ‹ Changeset detected

Latest commit: 59a1acc5697b56e3c73296dd245b3b36b9fff949

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@tanstack/svelte-query Patch
@tanstack/svelte-query-devtools Patch
@tanstack/svelte-query-persist-client Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

changeset-bot[bot] avatar Oct 25 '25 21:10 changeset-bot[bot]

Walkthrough

Replaces the previous $effect-based subscription in createBaseQuery.svelte.ts with an eagerly-initialized, SSR-aware unsubscribe and a watchChanges re-subscription path that calls observer.updateResult() when needed; adds onDestroy cleanup. Adds a patch changeset entry for the release.

Changes

Cohort / File(s) Change Summary
Changeset entry
โ€‹.changeset/thin-bugs-change.md
Adds a patch changeset for @tanstack/svelte-query documenting: "fix: support async Svelte". No code/API changes in that file.
Lifecycle / subscription refactor
packages/svelte-query/src/createBaseQuery.svelte.ts
Replaces $effect-based subscription with eager, SSR/client-aware unsubscribe initialization; introduces watchChanges to re-subscribe when isRestoring or observer change, calls observer.updateResult() on re-subscription, and registers onDestroy cleanup with try-catch for non-component contexts.

Sequence Diagram

sequenceDiagram
    participant Component as Svelte Component
    participant WatchChanges as watchChanges
    participant Observer
    participant OnDestroy as onDestroy

    rect rgb(245,245,255)
    Note over Component,Observer: Previous ($effect) pattern
    Component->>Observer: $effect establishes subscription
    Observer-->>Component: returns unsubscribe
    end

    rect rgb(235,250,235)
    Note over Component,WatchChanges: New pattern (eager init + watchChanges + onDestroy)
    Component->>Observer: initialize unsubscribe (eager, SSR-aware)
    Component->>WatchChanges: observe isRestoring / observer changes
    alt Re-subscribe needed
        WatchChanges->>Observer: call observer.updateResult()
        WatchChanges->>Observer: subscribe -> return new unsubscribe
    end
    Component->>OnDestroy: register cleanup
    OnDestroy->>Observer: call unsubscribe on destroy (safe try/catch)
    end

Estimated code review effort

๐ŸŽฏ 4 (Complex) | โฑ๏ธ ~50 minutes

  • Inspect SSR vs client branching and restoration-state logic.
  • Verify unsubscribe re-initialization correctness and edge cases where observer.updateResult() must run.
  • Check the try-catch on onDestroy to ensure no silent failures in non-component contexts.

Suggested reviewers

  • TkDodo

Poem

๐Ÿฐ I twitch my whiskers at subscriptions new,
I re-subscribe and wake the watcher too,
I guard the teardown with a careful sigh,
And hop away clean when onDestroy says goodbye. ๐Ÿฅ•

Pre-merge checks and finishing touches

โœ… Passed checks (3 passed)
Check name Status Explanation
Title Check โœ… Passed The PR title "fix: support async Svelte" directly aligns with the main objective of the changeset and code modifications. The raw summary confirms the change adds "fix support for async Svelte," and the PR description details the specific problem with Svelte 5 async components where $effect callbacks are deferred. The title is concise, uses the conventional "fix:" prefix, and clearly communicates the primary purpose to a developer reviewing history without requiring them to read the full description.
Description Check โœ… Passed The PR description comprehensively follows the repository template with all required sections properly completed. The Changes section provides detailed context about the Svelte 5 async behavior issue and explains why the fix is necessary. The Checklist section confirms both contributing guide compliance and local testing have been completed. The Release Impact section correctly indicates this change affects published code with a changeset generated, aligning with the raw summary showing a patch version bump entry.
Docstring Coverage โœ… Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
โœจ Finishing touches
  • [ ] ๐Ÿ“ Generate docstrings
๐Ÿงช Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment

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.

coderabbitai[bot] avatar Oct 25 '25 21:10 coderabbitai[bot]

View your CI Pipeline Execution โ†— for commit 59a1acc5697b56e3c73296dd245b3b36b9fff949

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... โœ… Succeeded 1m 13s View โ†—
nx run-many --target=build --exclude=examples/*... โœ… Succeeded 6s View โ†—

โ˜๏ธ Nx Cloud last updated this comment at 2025-11-01 19:52:59 UTC

nx-cloud[bot] avatar Nov 01 '25 19:11 nx-cloud[bot]

More templates

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@9810
@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@9810
@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@9810
@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@9810
@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@9810
@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@9810
@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@9810
@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@9810
@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@9810
@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@9810
@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@9810
@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@9810
@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@9810
@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@9810
@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@9810
@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@9810
@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@9810
@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@9810
@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@9810
@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@9810

commit: 59a1acc

pkg-pr-new[bot] avatar Nov 01 '25 19:11 pkg-pr-new[bot]

@lachlancollins could you take a look? This is blocking people from using Tanstack + async Svelte.

dummdidumm avatar Nov 14 '25 16:11 dummdidumm

This also solves an issue I have where queryFn is called twice in my test after I change the queryOptions. It looks like the effect is triggered when I change the queryOptions.

Some debugging statements in the code (without patch applied):

Component - queryOptions recalculated
createBaseQuery - effect triggered 
createBaseQuery - effect triggered 
queryFn called

Component - queryOptions recalculated
createBaseQuery - options changed
queryFn called
createBaseQuery - options or observer changed
createBaseQuery - effect triggered 
queryFn called

With the patch applied:

Component - queryOptions recalculated
queryFn called

Component - queryOptions recalculated
createBaseQuery - options changed
queryFn called
createBaseQuery - options or observer changed

tkvw avatar Nov 26 '25 21:11 tkvw