diff --git a/.changeset/curly-bags-attack.md b/.changeset/curly-bags-attack.md new file mode 100644 index 000000000..5148e0f46 --- /dev/null +++ b/.changeset/curly-bags-attack.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix hydration on slow connection diff --git a/packages/astro/src/runtime/server/astro-island.ts b/packages/astro/src/runtime/server/astro-island.ts index 710319aac..58eb7f2e0 100644 --- a/packages/astro/src/runtime/server/astro-island.ts +++ b/packages/astro/src/runtime/server/astro-island.ts @@ -56,17 +56,34 @@ declare const Astro: { document.addEventListener('astro:after-swap', this.unmount, { once: true }); } connectedCallback() { - if (!this.hasAttribute('await-children') || this.firstChild) { + if ( + !this.hasAttribute('await-children') || + document.readyState === 'interactive' || + document.readyState === 'complete' + ) { this.childrenConnectedCallback(); } else { // connectedCallback may run *before* children are rendered (ex. HTML streaming) - // If SSR children are expected, but not yet rendered, - // Wait with a mutation observer - new MutationObserver((_, mo) => { + // If SSR children are expected, but not yet rendered, wait with a mutation observer + // for a special marker inserted when rendering islands that signals the end of the island + const onConnected = () => { + document.removeEventListener('DOMContentLoaded', onConnected); mo.disconnect(); - // Wait until the next macrotask to ensure children are really rendered - setTimeout(() => this.childrenConnectedCallback(), 0); - }).observe(this, { childList: true }); + this.childrenConnectedCallback(); + }; + const mo = new MutationObserver(() => { + if ( + this.lastChild?.nodeType === Node.COMMENT_NODE && + this.lastChild.nodeValue === 'astro:end' + ) { + this.lastChild.remove(); + onConnected(); + } + }); + mo.observe(this, { childList: true }); + // in case the marker comment got stripped and the mutation observer waited indefinitely, + // also wait for DOMContentLoaded as a last resort + document.addEventListener('DOMContentLoaded', onConnected); } } async childrenConnectedCallback() { diff --git a/packages/astro/src/runtime/server/render/component.ts b/packages/astro/src/runtime/server/render/component.ts index bfb82ceda..158a88a07 100644 --- a/packages/astro/src/runtime/server/render/component.ts +++ b/packages/astro/src/runtime/server/render/component.ts @@ -360,6 +360,8 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr if (island.children) { island.props['await-children'] = ''; + // Marker to signal that Astro island children is completed while streaming + island.children += ``; } return {