From 97b47f6859efe04a28726db68e7f01890186e515 Mon Sep 17 00:00:00 2001 From: Julian Wolf Date: Tue, 24 Feb 2026 08:47:26 +0100 Subject: [PATCH 1/7] fix: include slot scripts in server island response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Components passed as slots to server:defer components had their scripts silently dropped because renderSlotToString separates HTML content from render instructions, and only the HTML was serialized into the island payload via content.toString(). This appends script instructions to the slot HTML before encryption, so the island response includes the scripts needed for interactivity. This may result in duplicate script injection when the same component is also used statically on the page. Deduplication is not feasible here due to the concurrent BufferedRenderer model — renderedScripts is not yet populated at the time island payloads are constructed. --- .../src/runtime/server/render/server-islands.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/runtime/server/render/server-islands.ts b/packages/astro/src/runtime/server/render/server-islands.ts index c4de293837ad..04a954766282 100644 --- a/packages/astro/src/runtime/server/render/server-islands.ts +++ b/packages/astro/src/runtime/server/render/server-islands.ts @@ -158,7 +158,17 @@ export class ServerIslandComponent { for (const name in this.slots) { if (name !== 'fallback') { const content = await renderSlotToString(this.result, this.slots[name]); - renderedSlots[name] = content.toString(); + let slotHtml = content.toString(); + // Append script instructions so that components passed as slots + // to server:defer components retain their scripts in the island response. + if (content.instructions) { + for (const instruction of content.instructions) { + if (instruction.type === 'script') { + slotHtml += instruction.content; + } + } + } + renderedSlots[name] = slotHtml; } } From 8c59537c11c504964a283fa82c597f1775789a47 Mon Sep 17 00:00:00 2001 From: Julian Wolf Date: Tue, 24 Feb 2026 09:14:51 +0100 Subject: [PATCH 2/7] add changeset --- .changeset/server-island-slot-scripts.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/server-island-slot-scripts.md diff --git a/.changeset/server-island-slot-scripts.md b/.changeset/server-island-slot-scripts.md new file mode 100644 index 000000000000..1838a4b5dca8 --- /dev/null +++ b/.changeset/server-island-slot-scripts.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix: include ` diff --git a/packages/astro/test/fixtures/server-islands/ssr/src/components/Wrapper.astro b/packages/astro/test/fixtures/server-islands/ssr/src/components/Wrapper.astro new file mode 100644 index 000000000000..f4c3cfc62527 --- /dev/null +++ b/packages/astro/test/fixtures/server-islands/ssr/src/components/Wrapper.astro @@ -0,0 +1,5 @@ +--- +--- +
+ +
diff --git a/packages/astro/test/fixtures/server-islands/ssr/src/pages/slot-with-script.astro b/packages/astro/test/fixtures/server-islands/ssr/src/pages/slot-with-script.astro new file mode 100644 index 000000000000..2db634722b90 --- /dev/null +++ b/packages/astro/test/fixtures/server-islands/ssr/src/pages/slot-with-script.astro @@ -0,0 +1,15 @@ +--- +import Wrapper from '../components/Wrapper.astro'; +import ScriptedCounter from '../components/ScriptedCounter.astro'; +--- + + + Slot with script + + + + +
Loading...
+
+ + diff --git a/packages/astro/test/server-islands.test.js b/packages/astro/test/server-islands.test.js index 330441add0c1..dfcaf8931e53 100644 --- a/packages/astro/test/server-islands.test.js +++ b/packages/astro/test/server-islands.test.js @@ -245,6 +245,20 @@ describe('Server islands', () => { assert.equal(fetchMatch.length, 2, 'should include props in the query string'); assert.equal(fetchMatch[1], '', 'should not include encrypted empty props'); }); + + it('includes script from slotted component in island response', async () => { + const res = await fixture.fetch('/slot-with-script'); + assert.equal(res.status, 200); + const html = await res.text(); + // Extract the island fetch URL from the page + const urlMatch = html.match(/fetch\('(\/_server-islands\/Wrapper\?[^']+)'/); + assert.ok(urlMatch, 'should have a server island fetch URL'); + const islandRes = await fixture.fetch(urlMatch[1]); + assert.equal(islandRes.status, 200); + const islandHtml = await islandRes.text(); + assert.ok(islandHtml.includes(' { From 8e846faab09202822474586bc77e9c61ed9709e5 Mon Sep 17 00:00:00 2001 From: Julian Wolf Date: Tue, 24 Feb 2026 10:44:21 +0100 Subject: [PATCH 6/7] Update .changeset/server-island-slot-scripts.md Co-authored-by: Florian Lefebvre --- .changeset/server-island-slot-scripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/server-island-slot-scripts.md b/.changeset/server-island-slot-scripts.md index 1838a4b5dca8..3066706588e2 100644 --- a/.changeset/server-island-slot-scripts.md +++ b/.changeset/server-island-slot-scripts.md @@ -2,4 +2,4 @@ 'astro': patch --- -Fix: include `