diff --git a/.changeset/warm-birds-cheer.md b/.changeset/warm-birds-cheer.md new file mode 100644 index 000000000000..84d25d272897 --- /dev/null +++ b/.changeset/warm-birds-cheer.md @@ -0,0 +1,5 @@ +--- +'@astrojs/svelte': patch +--- + +Fix Svelte component view transition state persistence diff --git a/packages/astro/e2e/fixtures/view-transitions/src/components/SvelteCounter.svelte b/packages/astro/e2e/fixtures/view-transitions/src/components/SvelteCounter.svelte index 6647a19ce7d0..669ee1a5afe7 100644 --- a/packages/astro/e2e/fixtures/view-transitions/src/components/SvelteCounter.svelte +++ b/packages/astro/e2e/fixtures/view-transitions/src/components/SvelteCounter.svelte @@ -1,5 +1,6 @@
- -
{count}
- + +
{prefix}{count}
+
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/island-svelte-one.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/island-svelte-one.astro new file mode 100644 index 000000000000..c60dfa9c9d10 --- /dev/null +++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/island-svelte-one.astro @@ -0,0 +1,11 @@ +--- +import Counter from '../components/SvelteCounter.svelte'; +import Layout from '../components/Layout.astro'; +export const prerender = false; + +--- + +

Page 1

+ go to 2 + +
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/island-svelte-two.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/island-svelte-two.astro new file mode 100644 index 000000000000..63222bacbc39 --- /dev/null +++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/island-svelte-two.astro @@ -0,0 +1,11 @@ +--- +import Counter from '../components/SvelteCounter.svelte'; +import Layout from '../components/Layout.astro'; +export const prerender = false; + +--- + +

Page 2

+ go to 1 + +
diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js index 64e0e3b793ae..a94c50d0363f 100644 --- a/packages/astro/e2e/view-transitions.test.js +++ b/packages/astro/e2e/view-transitions.test.js @@ -543,8 +543,8 @@ test.describe('View Transitions', () => { const pageTitle = page.locator('.page'); await expect(pageTitle).toHaveText('Island 2'); }); - - test('Solid Islands can persist using transition:persist', async ({ page, astro }) => { + + test('Solid Islands can persist using transition:persist', async ({ page, astro }) => { // Go to page 1 await page.goto(astro.resolveUrl('/island-solid-one')); let cnt = page.locator('.counter pre'); @@ -569,6 +569,24 @@ test.describe('View Transitions', () => { await expect(cnt).toHaveText('A1'); }); + test('Svelte Islands can persist using transition:persist', async ({ page, astro }) => { + // Go to page 1 + await page.goto(astro.resolveUrl('/island-svelte-one')); + let cnt = page.locator('.counter pre'); + await expect(cnt).toHaveText('A0'); + + await page.click('.increment'); + await expect(cnt).toHaveText('A1'); + + // Navigate to page 2 + await page.click('#click-two'); + let p = page.locator('#island-two'); + await expect(p).toBeVisible(); + cnt = page.locator('.counter pre'); + // Count should remain, but the prefix should be updated + await expect(cnt).toHaveText('B1'); + }); + test('Vue Islands can persist using transition:persist', async ({ page, astro }) => { // Go to page 1 await page.goto(astro.resolveUrl('/island-vue-one')); diff --git a/packages/integrations/svelte/client-v5.js b/packages/integrations/svelte/client-v5.js index fda68ee54878..78bab3ea34b3 100644 --- a/packages/integrations/svelte/client-v5.js +++ b/packages/integrations/svelte/client-v5.js @@ -1,5 +1,7 @@ import { createRawSnippet, hydrate, mount, unmount } from 'svelte'; +const existingApplications = new WeakMap(); + export default (element) => { return async (Component, props, slotted, { client }) => { if (!element.hasAttribute('ssr')) return; @@ -21,15 +23,23 @@ export default (element) => { } const bootstrap = client !== 'only' ? hydrate : mount; - - const component = bootstrap(Component, { - target: element, - props: { + if (existingApplications.has(element)) { + existingApplications.get(element).$set({ ...props, children, $$slots, - }, - }); + }); + } else { + const component = bootstrap(Component, { + target: element, + props: { + ...props, + children, + $$slots, + }, + }); + existingApplications.set(element, component); + } element.addEventListener('astro:unmount', () => unmount(component), { once: true }); }; diff --git a/packages/integrations/svelte/client.js b/packages/integrations/svelte/client.js index 005bbe9da331..288c7a661b6f 100644 --- a/packages/integrations/svelte/client.js +++ b/packages/integrations/svelte/client.js @@ -3,6 +3,8 @@ const noop = () => {}; let originalConsoleWarning; let consoleFilterRefs = 0; +const existingApplications = new WeakMap(); + export default (element) => { return (Component, props, slotted, { client }) => { if (!element.hasAttribute('ssr')) return; @@ -14,18 +16,23 @@ export default (element) => { try { if (import.meta.env.DEV) useConsoleFilter(); - const component = new Component({ - target: element, - props: { - ...props, - $$slots: slots, - $$scope: { ctx: [] }, - }, - hydrate: client !== 'only', - $$inline: true, - }); + if (existingApplications.has(element)) { + existingApplications.get(element).$set({ ...props, $$slots: slots, $$scope: { ctx: [] } }); + } else { + const component = new Component({ + target: element, + props: { + ...props, + $$slots: slots, + $$scope: { ctx: [] }, + }, + hydrate: client !== 'only', + $$inline: true, + }); + existingApplications.set(element, component); - element.addEventListener('astro:unmount', () => component.$destroy(), { once: true }); + element.addEventListener('astro:unmount', () => component.$destroy(), { once: true }); + } } finally { if (import.meta.env.DEV) finishUsingConsoleFilter(); }