From 622e49f070600c674c6a1c6e24c59f9e0060a543 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Sat, 7 Feb 2026 17:05:18 +0100 Subject: [PATCH 1/3] fix: don't wait for remote functions that are not awaited in the template Fixes #15200 --- .changeset/large-squids-pull.md | 5 +++++ packages/kit/src/runtime/server/page/render.js | 17 ++++++++++++++++- .../routes/remote/query-boundary/+page.svelte | 14 ++++++++++++++ .../routes/remote/query-boundary/data.remote.js | 10 ++++++++++ packages/kit/test/apps/async/test/test.js | 15 +++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 .changeset/large-squids-pull.md create mode 100644 packages/kit/test/apps/async/src/routes/remote/query-boundary/+page.svelte create mode 100644 packages/kit/test/apps/async/src/routes/remote/query-boundary/data.remote.js diff --git a/.changeset/large-squids-pull.md b/.changeset/large-squids-pull.md new file mode 100644 index 000000000000..ca7f00a02d16 --- /dev/null +++ b/.changeset/large-squids-pull.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't wait for remote functions that are not awaited in the template diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 4a6b80be0373..966748eddbcd 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -493,7 +493,22 @@ export async function render_response({ if (!info.id) continue; for (const key in cache) { - remote[create_remote_key(info.id, key)] = await cache[key]; + // Don't block the response on pending remote data — if a query + // hasn't settled yet (e.g. caught by a svelte:boundary), skip it + // and let the client fetch it instead via the remote function stub. + const result = await Promise.race([ + Promise.resolve(cache[key]).then( + (v) => /** @type {const} */ ({ settled: true, value: v }), + () => /** @type {const} */ ({ settled: false }) + ), + new Promise((resolve) => { + queueMicrotask(() => resolve(/** @type {const} */ ({ settled: false }))); + }) + ]); + + if (result.settled) { + remote[create_remote_key(info.id, key)] = result.value; + } } } diff --git a/packages/kit/test/apps/async/src/routes/remote/query-boundary/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/query-boundary/+page.svelte new file mode 100644 index 000000000000..6b4aa6de9dcc --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/remote/query-boundary/+page.svelte @@ -0,0 +1,14 @@ + + +

{await get_fast_data()}

+ + {#snippet pending()} +

loading delayed

+ {/snippet} +

{await delayed}

+
diff --git a/packages/kit/test/apps/async/src/routes/remote/query-boundary/data.remote.js b/packages/kit/test/apps/async/src/routes/remote/query-boundary/data.remote.js new file mode 100644 index 000000000000..c769ac5e749d --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/remote/query-boundary/data.remote.js @@ -0,0 +1,10 @@ +import { query } from '$app/server'; + +export const get_delayed_data = query(async () => { + await new Promise((resolve) => setTimeout(resolve, 2000)); + return 'delayed data'; +}); + +export const get_fast_data = query(() => { + return 'fast data'; +}); diff --git a/packages/kit/test/apps/async/test/test.js b/packages/kit/test/apps/async/test/test.js index 01b2f937d3c2..9b3f97205fd7 100644 --- a/packages/kit/test/apps/async/test/test.js +++ b/packages/kit/test/apps/async/test/test.js @@ -482,4 +482,19 @@ test.describe('remote functions', () => { }) ); }); + + test('query stored as variable does not block SSR inside boundary', async ({ + page, + javaScriptEnabled + }) => { + await page.goto('/remote/query-boundary'); + + await expect(page.locator('#delayed-pending')).toHaveText('loading delayed'); + + if (javaScriptEnabled) { + await expect(page.locator('#delayed-result')).toHaveText('delayed data', { + timeout: 5000 + }); + } + }); }); From e96937252862a0b0ccc896dcd5f511be5249e3a8 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Sat, 7 Feb 2026 21:18:26 +0100 Subject: [PATCH 2/3] fix --- .../kit/src/runtime/server/page/render.js | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 966748eddbcd..72e935e70065 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -493,21 +493,29 @@ export async function render_response({ if (!info.id) continue; for (const key in cache) { - // Don't block the response on pending remote data — if a query - // hasn't settled yet (e.g. caught by a svelte:boundary), skip it - // and let the client fetch it instead via the remote function stub. - const result = await Promise.race([ - Promise.resolve(cache[key]).then( - (v) => /** @type {const} */ ({ settled: true, value: v }), - () => /** @type {const} */ ({ settled: false }) - ), - new Promise((resolve) => { - queueMicrotask(() => resolve(/** @type {const} */ ({ settled: false }))); - }) - ]); - - if (result.settled) { - remote[create_remote_key(info.id, key)] = result.value; + const remote_key = create_remote_key(info.id, key); + + if (event_state.refreshes?.[remote_key] !== undefined) { + // This entry was refreshed/set by a command or form action. + // Always await it so the mutation result is serialized. + remote[remote_key] = await cache[key]; + } else { + // Don't block the response on pending remote data - if a query + // hasn't settled yet, it wasn't awaited in the template (or is behind a pending boundary), + const result = await Promise.race([ + Promise.resolve(cache[key]).then( + (v) => /** @type {const} */ ({ settled: true, value: v }), + (e) => /** @type {const} */ ({ settled: true, error: e }) + ), + new Promise((resolve) => { + queueMicrotask(() => resolve(/** @type {const} */ ({ settled: false }))); + }) + ]); + + if (result.settled) { + if ('error' in result) throw result.error; + remote[remote_key] = result.value; + } } } } From 0e19c157edc50c3520d58412b1ece06280747473 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:31:41 +0100 Subject: [PATCH 3/3] Update packages/kit/src/runtime/server/page/render.js Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> --- packages/kit/src/runtime/server/page/render.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 72e935e70065..f7d1c860bc04 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -501,7 +501,7 @@ export async function render_response({ remote[remote_key] = await cache[key]; } else { // Don't block the response on pending remote data - if a query - // hasn't settled yet, it wasn't awaited in the template (or is behind a pending boundary), + // hasn't settled yet, it wasn't awaited in the template (or is behind a pending boundary). const result = await Promise.race([ Promise.resolve(cache[key]).then( (v) => /** @type {const} */ ({ settled: true, value: v }),