Skip to content

Commit ecadb6b

Browse files
authoredMar 20, 2025··
fix(preact,svelte): empty target container before rendering client:only island (#13470)
* fix(preact,svelte): empty target container before rendering `client:only` island * Remove log
1 parent 62595a0 commit ecadb6b

File tree

5 files changed

+41
-5
lines changed

5 files changed

+41
-5
lines changed
 

‎.changeset/deep-mammals-watch.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@astrojs/preact': patch
3+
'@astrojs/svelte': patch
4+
---
5+
6+
Hides fallback content when rendering `client:only` island

‎packages/astro/e2e/client-only.test.js

+16
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ test.describe('Client only', () => {
2020
const counter = await page.locator('#react-counter');
2121
await expect(counter, 'component is visible').toBeVisible();
2222

23+
const fallback = await page.locator('[data-fallback=react]');
24+
await expect(fallback, 'fallback content is hidden').not.toBeVisible();
25+
2326
const count = await counter.locator('pre');
2427
await expect(count, 'initial count is 0').toHaveText('0');
2528

@@ -38,6 +41,10 @@ test.describe('Client only', () => {
3841
const counter = await page.locator('#preact-counter');
3942
await expect(counter, 'component is visible').toBeVisible();
4043

44+
45+
const fallback = await page.locator('[data-fallback=preact]');
46+
await expect(fallback, 'fallback content is hidden').not.toBeVisible();
47+
4148
const count = await counter.locator('pre');
4249
await expect(count, 'initial count is 0').toHaveText('0');
4350

@@ -56,6 +63,9 @@ test.describe('Client only', () => {
5663
const counter = await page.locator('#solid-counter');
5764
await expect(counter, 'component is visible').toBeVisible();
5865

66+
const fallback = await page.locator('[data-fallback=solid]');
67+
await expect(fallback, 'fallback content is hidden').not.toBeVisible();
68+
5969
const count = await counter.locator('pre');
6070
await expect(count, 'initial count is 0').toHaveText('0');
6171

@@ -74,6 +84,9 @@ test.describe('Client only', () => {
7484
const counter = await page.locator('#vue-counter');
7585
await expect(counter, 'component is visible').toBeVisible();
7686

87+
const fallback = await page.locator('[data-fallback=vue]');
88+
await expect(fallback, 'fallback content is hidden').not.toBeVisible();
89+
7790
const count = await counter.locator('pre');
7891
await expect(count, 'initial count is 0').toHaveText('0');
7992

@@ -92,6 +105,9 @@ test.describe('Client only', () => {
92105
const counter = await page.locator('#svelte-counter');
93106
await expect(counter, 'component is visible').toBeVisible();
94107

108+
const fallback = await page.locator('[data-fallback=svelte]');
109+
await expect(fallback, 'fallback content is hidden').not.toBeVisible();
110+
95111
const count = await counter.locator('pre');
96112
await expect(count, 'initial count is 0').toHaveText('0');
97113

‎packages/astro/e2e/fixtures/client-only/src/pages/index.astro

+5
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,27 @@ import VueCounter from '../components/vue/VueCounter.vue';
1919
<main>
2020
<react.Counter id="react-counter" client:only="react">
2121
<h1>react</h1>
22+
<p slot="fallback" data-fallback="react">Loading React...</p>
2223
</react.Counter>
2324

2425
<PreactCounter id="preact-counter" client:only="preact">
2526
<h1>preact</h1>
27+
<p slot="fallback" data-fallback="preact">Loading Preact...</p>
2628
</PreactCounter>
2729

2830
<SolidCounter id="solid-counter" client:only="solid-js">
2931
<h1>solid</h1>
32+
<p slot="fallback" data-fallback="solid">Loading Solid...</p>
3033
</SolidCounter>
3134

3235
<VueCounter id="vue-counter" client:only="vue">
3336
<h1>vue</h1>
37+
<p slot="fallback" data-fallback="vue">Loading Vue...</p>
3438
</VueCounter>
3539

3640
<SvelteCounter id="svelte-counter" client:only="svelte">
3741
<h1>svelte</h1>
42+
<p slot="fallback" data-fallback="svelte">Loading Svelte...</p>
3843
</SvelteCounter>
3944
</main>
4045
</body>

‎packages/integrations/preact/src/client.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,19 @@ export default (element: HTMLElement) =>
4949
}
5050
}
5151

52-
const bootstrap = client !== 'only' ? hydrate : render;
53-
54-
bootstrap(
55-
h(Component, props, children != null ? h(StaticHtml, { value: children }) : children),
56-
element,
52+
const child = h(
53+
Component,
54+
props,
55+
children != null ? h(StaticHtml, { value: children }) : children,
5756
);
5857

58+
if (client === 'only') {
59+
element.innerHTML = '';
60+
render(child, element);
61+
} else {
62+
hydrate(child, element);
63+
}
64+
5965
// Preact has no "unmount" option, but you can use `render(null, element)`
6066
element.addEventListener('astro:unmount', () => render(null, element), { once: true });
6167
};

‎packages/integrations/svelte/client.svelte.js

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ export default (element) => {
6161
function createComponent(Component, target, props, shouldHydrate) {
6262
let propsState = $state(props);
6363
const bootstrap = shouldHydrate ? hydrate : mount;
64+
if(!shouldHydrate) {
65+
target.innerHTML = '';
66+
}
6467
const component = bootstrap(Component, { target, props: propsState });
6568
return {
6669
setProps(newProps) {

0 commit comments

Comments
 (0)
Please sign in to comment.