From 3a5a11186f685fd4557ab3cfc465cf606ddb60ab Mon Sep 17 00:00:00 2001 From: svelte-triage-bot Date: Sat, 31 Oct 2026 06:50:06 +0000 Subject: [PATCH] fix: use null source for client-only enter navigations --- .changeset/early-otters-enter.md | 5 ++++ packages/kit/src/runtime/client/client.js | 15 +++++++----- .../after-navigate-enter/[slug]/+page.svelte | 3 +++ .../routes/no-ssr/after-navigate/+page.svelte | 6 +++++ .../basics/test/playwright/client.test.js | 24 +++++++++++++++++++ .../playwright/cross-platform/client.test.js | 1 + 6 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 .changeset/early-otters-enter.md diff --git a/.changeset/early-otters-enter.md b/.changeset/early-otters-enter.md new file mode 100644 index 000000000000..2946ef330267 --- /dev/null +++ b/.changeset/early-otters-enter.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: set initial navigation's `from` to null when SSR is disabled diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 45bd73155aaf..6e18b950ea40 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -3755,12 +3755,15 @@ function create_navigation( /** @type {(Navigation | AfterNavigate) & { type: T }} */ const navigation = /** @type {any} */ ({ - from: { - params: current.params, - route: { id: current.route?.id ?? null }, - url: current.url, - scroll: scroll_state() - }, + from: + type === 'enter' + ? null + : { + params: current.params, + route: { id: current.route?.id ?? null }, + url: current.url, + scroll: scroll_state() + }, to: url && { params: target ? target.params : (intent?.params ?? null), route: { id: target ? (target.route?.id ?? null) : (intent?.route?.id ?? null) }, diff --git a/packages/kit/test/apps/basics/src/routes/navigation-lifecycle/after-navigate-enter/[slug]/+page.svelte b/packages/kit/test/apps/basics/src/routes/navigation-lifecycle/after-navigate-enter/[slug]/+page.svelte index 668528f7791b..20419e78422b 100644 --- a/packages/kit/test/apps/basics/src/routes/navigation-lifecycle/after-navigate-enter/[slug]/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/navigation-lifecycle/after-navigate-enter/[slug]/+page.svelte @@ -5,8 +5,10 @@ let route_id; /** @type {string | undefined} */ let params; + let from = ''; afterNavigate((navigation) => { + from = JSON.stringify(navigation.from); route_id = navigation.to?.route.id; params = JSON.stringify(navigation.to?.params); }); @@ -14,3 +16,4 @@

route: {route_id}

params: {params}

+
{from}
diff --git a/packages/kit/test/apps/basics/src/routes/no-ssr/after-navigate/+page.svelte b/packages/kit/test/apps/basics/src/routes/no-ssr/after-navigate/+page.svelte index 0e99d8405c54..ff8fc6e5fa22 100644 --- a/packages/kit/test/apps/basics/src/routes/no-ssr/after-navigate/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/no-ssr/after-navigate/+page.svelte @@ -3,11 +3,17 @@ let count = 0; let type = ''; + let from = ''; + let to = ''; afterNavigate((event) => { count += 1; type = event.type; + from = JSON.stringify(event.from); + to = JSON.stringify(event.to); });

{type.toString()} {count}

+
{from}
+
{to}
diff --git a/packages/kit/test/apps/basics/test/playwright/client.test.js b/packages/kit/test/apps/basics/test/playwright/client.test.js index de86366c610a..b4bc1ba14dab 100644 --- a/packages/kit/test/apps/basics/test/playwright/client.test.js +++ b/packages/kit/test/apps/basics/test/playwright/client.test.js @@ -503,6 +503,30 @@ test.describe('SPA mode / no SSR', () => { ); }); + test('afterNavigate enter navigation has no from target when SSR is disabled', async ({ + page, + app + }) => { + const path = '/no-ssr/after-navigate'; + await page.goto(path); + await expect(page.locator('pre.from')).toHaveText('null'); + const target = { + params: {}, + route: { id: path }, + url: page.url(), + scroll: { x: 0, y: 0 } + }; + expect(JSON.parse(await page.locator('pre.to').innerText())).toEqual(target); + + await app.goto(`${path}?next`); + await expect(page.locator('p')).toHaveText('goto 2'); + expect(JSON.parse(await page.locator('pre.from').innerText())).toEqual(target); + + await page.reload(); + await expect(page.locator('p')).toHaveText('enter 1'); + await expect(page.locator('pre.from')).toHaveText('null'); + }); + test('afterNavigate is only called once during start', async ({ page }) => { await page.goto('/no-ssr/after-navigate'); await expect(page.locator('p')).toHaveText('enter 1'); diff --git a/packages/kit/test/apps/basics/test/playwright/cross-platform/client.test.js b/packages/kit/test/apps/basics/test/playwright/cross-platform/client.test.js index 8325d221bf6f..3994136213d0 100644 --- a/packages/kit/test/apps/basics/test/playwright/cross-platform/client.test.js +++ b/packages/kit/test/apps/basics/test/playwright/cross-platform/client.test.js @@ -323,6 +323,7 @@ test.describe('Navigation lifecycle functions', () => { 'route: /navigation-lifecycle/after-navigate-enter/[slug]' ); expect(await page.textContent('p')).toBe('params: {"slug":"x"}'); + await expect(page.locator('pre')).toHaveText('null'); }); test('onNavigate calls callback', async ({ page, clicknav }) => {