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 @@
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();
}