diff --git a/.changeset/grumpy-hotels-heal.md b/.changeset/grumpy-hotels-heal.md
new file mode 100644
index 000000000..f171c91fb
--- /dev/null
+++ b/.changeset/grumpy-hotels-heal.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Prevent client:only styles from being removed in dev (View Transitions)
diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro
index 8fb506037..822be5ccb 100644
--- a/packages/astro/components/ViewTransitions.astro
+++ b/packages/astro/components/ViewTransitions.astro
@@ -113,6 +113,11 @@ const { fallback = 'animate' } = Astro.props as Props;
const parser = new DOMParser();
+ // A noop element used to prevent styles from being removed
+ if(import.meta.env.DEV) {
+ var noopEl: string | undefined = document.createElement('div');
+ }
+
async function updateDOM(doc: Document, loc: URL, state?: State, fallback?: Fallback) {
// Check for a head element that should persist, either because it has the data
// attribute or is a link el.
@@ -139,6 +144,16 @@ const { fallback = 'animate' } = Astro.props as Props;
}
}
}
+ // Only run this in dev. This will get stripped from production builds and is not needed.
+ if(import.meta.env.DEV) {
+ if(el.tagName === 'STYLE' && el.dataset.viteDevId) {
+ const devId = el.dataset.viteDevId;
+ // If this same style tag exists, remove it from the new page
+ return doc.querySelector(`style[data-astro-dev-id="${devId}"]`)
+ // Otherwise, keep it anyways. This is client:only styles.
+ || noopEl;
+ }
+ }
return null;
};
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-one.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-one.astro
new file mode 100644
index 000000000..a8d5e8995
--- /dev/null
+++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-one.astro
@@ -0,0 +1,10 @@
+---
+import Layout from '../components/Layout.astro';
+import Island from '../components/Island';
+---
+
+ go to page 2
+
+ message here
+
+
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-two.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-two.astro
new file mode 100644
index 000000000..884ec4683
--- /dev/null
+++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-two.astro
@@ -0,0 +1,10 @@
+---
+import Layout from '../components/Layout.astro';
+import Island from '../components/Island';
+---
+
+ Page 2
+
+ message here
+
+
diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js
index 80a180608..bd8ad5c36 100644
--- a/packages/astro/e2e/view-transitions.test.js
+++ b/packages/astro/e2e/view-transitions.test.js
@@ -565,4 +565,24 @@ test.describe('View Transitions', () => {
p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1');
});
+
+ test("client:only styles are retained on transition", async ({ page, astro }) => {
+ const totalExpectedStyles = 8;
+
+ // Go to page 1
+ await page.goto(astro.resolveUrl('/client-only-one'));
+ let msg = page.locator('.counter-message');
+ await expect(msg).toHaveText('message here');
+
+ let styles = await page.locator('style').all();
+ expect(styles.length).toEqual(totalExpectedStyles);
+
+ await page.click('#click-two');
+
+ let pageTwo = page.locator('#page-two');
+ await expect(pageTwo, 'should have content').toHaveText('Page 2');
+
+ styles = await page.locator('style').all();
+ expect(styles.length).toEqual(totalExpectedStyles, 'style count has not changed');
+ });
});