Skip to content

Conversation

@harshitydv1
Copy link

@harshitydv1 harshitydv1 commented Dec 4, 2025

Fixes #17304

Problem Description

When using async in SvelteKit with preloading data (via data-sveltekit-preload-data or preloadData()), $effect callbacks stop firing even though $inspect shows the values are updating correctly.

Reproduction

The issue can be reproduced with the StackBlitz example: https://stackblitz.com/edit/sveltekit-reactivity-loss

Steps to reproduce:

  1. Set data-sveltekit-preload-data="hover" on a link
  2. Hover over the link to trigger preloading
  3. Click a button that should trigger an $effect to open a dialog
  4. The dialog doesn't open because the $effect doesn't fire

Root Cause

The issue occurs in the batch system's revive() method in packages/svelte/src/internal/client/reactivity/batch.js.

When SvelteKit's preload functionality is used, it creates a "fork" to speculatively load data. When this fork is committed, it calls batch.revive() to reschedule any deferred effects. However, the revive() method wasn't ensuring that the batch was properly activated before scheduling effects.

The sequence of events:

  1. Async operations (like preload) defer effects to #dirty_effects and #maybe_dirty_effects arrays
  2. Effects are marked as CLEAN to allow them to be rescheduled later
  3. When async work completes, revive() is called to reschedule these effects
  4. BUG: If the batch wasn't the current batch, schedule_effect() would add effects to queued_root_effects, but they wouldn't be processed correctly because the batch context wasn't active

Solution

The fix ensures that:

  1. The batch is activated before scheduling deferred effects
  2. The batch is properly flushed when there are queued effects
  3. The batch is deactivated after processing if it wasn't the current batch initially

Changes Made

Modified the revive() method in batch.js to:

  • Check if the batch is the current batch before scheduling effects
  • Activate the batch if it's not current (critical for fork commits)
  • Ensure flushing happens when there are queued root effects
  • Deactivate the batch after processing if it wasn't current initially

Impact

This fix ensures that effects are properly revived after async operations complete, which is critical for:

  • SvelteKit's preload functionality
  • Any code using the fork() API
  • Async state management in Svelte 5

The change is minimal and focused on ensuring the correct batch context

Copilot AI review requested due to automatic review settings December 4, 2025 18:41
@changeset-bot
Copy link

changeset-bot bot commented Dec 4, 2025

🦋 Changeset detected

Latest commit: 89c2a32

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

This PR includes changesets to release 1 package
Name Type
svelte 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

Copilot finished reviewing on behalf of harshitydv1 December 4, 2025 18:44
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an issue where $effect callbacks stop firing after async operations in SvelteKit's preload functionality. The root cause was that when forks are committed, the revive() method in the batch system wasn't ensuring proper batch activation before scheduling deferred effects.

Key Changes:

  • Modified the revive() method to activate the batch before scheduling effects when the batch is not current
  • Added conditional logic to determine when to flush based on pending work or queued effects
  • Added deactivation after processing when the batch wasn't current initially

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/svelte/src/internal/client/reactivity/batch.js Modified revive() method to handle batch activation/deactivation and conditional flushing when reviving deferred effects after async operations
.changeset/fix-async-reactivity-17304.md Added changeset documenting the patch fix for deferred effects not firing after async operations

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

async breaks reactivity with SvelteKit's preload

1 participant