Skip to content

fix: svelte:element hydration in raw text elements#17974

Open
sudip-kumar-prasad wants to merge 6 commits intosveltejs:mainfrom
sudip-kumar-prasad:fix-svelte-element-hydration
Open

fix: svelte:element hydration in raw text elements#17974
sudip-kumar-prasad wants to merge 6 commits intosveltejs:mainfrom
sudip-kumar-prasad:fix-svelte-element-hydration

Conversation

@sudip-kumar-prasad
Copy link

@sudip-kumar-prasad sudip-kumar-prasad commented Mar 20, 2026

Fix hydration error for raw text elements in svelte:element

Hydration was failing when using svelte:element with raw text elements like <style> and <script>.

This happened because $.append calls hydrate_next(), which expects a sibling node to move to. However, SSR doesn’t insert hydration markers inside raw text elements, so hydrate_next() ended up hitting a null sibling and throwing.

This change adds a temporary comment node during hydration so hydrate_next() has a valid sibling to work with. The comment is removed immediately after, so it doesn’t affect the final DOM.

Also added a regression test in runtime-runes to cover this case.

Copilot AI review requested due to automatic review settings March 20, 2026 10:54
@changeset-bot
Copy link

changeset-bot bot commented Mar 20, 2026

🦋 Changeset detected

Latest commit: a2a5323

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
svelte Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a hydration crash when <svelte:element> is used with raw-text elements (e.g. style/script) by ensuring hydration has a sentinel sibling to advance to, and adds a runtime-runes sample to cover the scenario.

Changes:

  • Add a temporary comment node during hydration for raw-text <svelte:element> so hydrate_next() can safely advance, then remove it.
  • Add a new runtime-runes sample exercising <svelte:element this="style"> with hydration enabled.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
packages/svelte/src/internal/client/dom/blocks/svelte-element.js Injects/removes a temporary comment node to avoid hydration traversal errors in raw-text elements.
packages/svelte/tests/runtime-runes/samples/svelte-element-style-hydration/main.svelte New sample component rendering CSS via <svelte:element>.
packages/svelte/tests/runtime-runes/samples/svelte-element-style-hydration/_config.js New sample config enabling hydration + expected HTML.
Comments suppressed due to low confidence (1)

packages/svelte/src/internal/client/dom/blocks/svelte-element.js:105

  • The temporary comment is appended before determining child_anchor. For empty raw-text elements (e.g. <style></style>, <script></script>, <textarea></textarea>), this makes get_first_child(element) return the injected comment instead of null, so hydration stays enabled and hydrate_next() can still throw when it hits a missing sibling. Consider computing child_anchor first, and only appending the comment when child_anchor !== null and it has no next sibling (i.e. when there is existing SSR content that needs a sentinel).
if (hydrating && is_raw_text_element(next_tag)) {	// prevent hydration glitches	comment = document.createComment('');	element.append(comment);	}	// If hydrating, use the existing ssr comment as the anchor so that the	// inner open and close methods can pick up the existing nodes correctly	var child_anchor = hydrating	? get_first_child(element)	: element.appendChild(create_text()); 

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sudip-kumar-prasad
Copy link
Author

Hi 👋

This PR is ready for review. The hydration issue for raw text elements has been fixed and covered with a regression test.

All feedback has been addressed, and a changeset is included.

Could a maintainer please take a look and approve the workflow so remaining checks can run?

Thanks!

@sudip-kumar-prasad
Copy link
Author

Hi 👋

Just a quick update — I’ve addressed all feedback, synced with main, and updated the test expectations based on the current behavior after hydration.

Everything should be ready now. Please let me know if any changes are needed!

Would appreciate a review when you get time. Thanks!

@Rich-Harris
Copy link
Member

Thank you but I'm not sure I understand what problem this addresses? I'm not seeing any hydration errors with script/style elements. Do you have an issue or a repro?

@sudip-kumar-prasad
Copy link
Author

sudip-kumar-prasad commented Mar 21, 2026

Hi Rich! Thanks for taking a look.

This happens specifically during hydration (SSR → client) when svelte:element renders a raw-text element like <style> or <script>.

Minimal repro

<!-- App.svelte --> <script> let tag = 'style'; const css = 'body { color: red; }'; </script> <div> <svelte:element this={tag}>{css}</svelte:element> </div> 

Hydrating SSR output:

import { hydrate } from 'svelte'; import App from './App.svelte'; document.body.innerHTML = '<div><style>body { color: red; }</style></div>'; hydrate(App, { target: document.body.firstChild }); // hydration mismatch 

Root cause

Raw-text elements (style, script, etc.) do not get hydration markers (, ) in SSR output.
During hydration, render_fn runs and eventually calls $.append().
$.append() → hydrate_next() → get_next_sibling(hydrate_node)
But inside <style>, the text node has no next sibling (no closing marker).
This results in set_hydrate_node(null) → hydration_mismatch.

Fix

The fix temporarily inserts a comment node inside the raw-text element before render_fn runs, so hydrate_next() has a valid sibling to advance to. It’s then removed in a finally block, so it doesn’t affect the final DOM.

The regression test (svelte-element-style-hydration) specifically covers the mode: ['hydrate'] path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants