Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hydration mismatch not reconciling properly #14909

Open
pjebs opened this issue Jan 6, 2025 · 13 comments
Open

Hydration mismatch not reconciling properly #14909

pjebs opened this issue Jan 6, 2025 · 13 comments

Comments

@pjebs
Copy link

pjebs commented Jan 6, 2025

Describe the bug

For the purposes of explaining the bug:

Let's say my index.html contains this:

<html>
  <body>
    <div id="app">
    SAMPLE BODY
    </div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

Let's say svelte will manage the app div on the client.

import { mount } from 'svelte'
import AppWrapper from './AppWrapper.svelte'

const app = mount(AppWrapper, {
  target: document.getElementById('app')!,
})

export default app
  1. I am trying to do SSR for Svelte on a non-JS programming language server. This is quite challenging.
  2. My solution is kick-start a JS runtime on my server (eg. v8).
  3. Run the Svelte code in the server's JS runtime.
  4. Parse the rendered html output and copy the entire DOM structure within the app div tag. (NB: "SAMPLE BODY" text was just an example, not the real rendered html output. Also that text was not in the index.html file passed to the JS runtime!)
  5. When it's time to serve the actual html file and svelte-generated js file, I replace the contents within the app div tag with the html from step 4.
  6. CSR happens.
  7. For me, the client-side rendering is the "source-of-truth" and should always be preferred because it is official and not hacky.

The BUG

  1. For some reason, svelte appends the client-side rendered DOM inside the app tag instead of replacing it. (i.e. for the example at the top, it will say "SAMPLE BODY" AND THEN the acual client-side rendered DOM. (Note: React replaces instead of appends)
  2. There is also NO hydration_mismatch errors on the console.

Extra Issue in Documentation:

There are 3 relevant errors:

  1. https://svelte.dev/docs/svelte/runtime-warnings#Client-warnings-hydration_attribute_changed
  2. https://svelte.dev/docs/svelte/runtime-warnings#Client-warnings-hydration_html_changed
  3. https://svelte.dev/docs/svelte/runtime-warnings#Client-warnings-hydration_mismatch

The docs state a "hacky" way to force a client-side update:

<script>
	let { markup } = $props();

	if (typeof window !== 'undefined') {
		// stash the value...
		const initial = markup;

		// unset it...
		markup = undefined;

		$effect(() => {
			// ...and reset after we've mounted
			markup = initial;
		});
	}
</script>

{@html markup}

Proposal 1
I propose that there is an option added to the compiler to ALWAYS PREFER CLIENT SIDE RENDERING for scenarios such as mine.

Proposal 2

In the docs, it should clearly state EXACTLY which attributes the below applies to (i.e. what else other than src?)

hydration_attribute_changed:

Certain attributes like src on an element will not be repaired during hydration, i.e. the server value will be kept.

hydration_html_changed:

If the {@html ...} value changes between the server and the client, it will not be repaired during hydration, i.e. the server value will be kept.

Reproduction

N/A (requires code from another programming language)

Logs

No response

System Info

Latest version of Svelte

Severity

blocking all usage of svelte

Thanks @tanhauhau for your video on hydration.

@paoloricciuti
Copy link
Member

You should use hydrate rather than mount

@pjebs
Copy link
Author

pjebs commented Jan 6, 2025

@paoloricciuti That seemed to work.

Q1. mount was from the template code that came from svelte's getting started CLI generator. Perhaps the template should be replaced with hydrate.
Q2 Why does mount append. Should it just spit out an error in my scenario?
Q3 Even after changing to hydrate, I still didn't get a hydration_mismatch error in the console when clearly there is a mismatch.

@paoloricciuti
Copy link
Member

I think the CLI generator assume that you will have no content of your own and just an empty div. In that case mount is correct because there's no html to hydrate. This is also why I don't think it should spit out an error...mount "adds" to the element without even looking at the content and I think it's fair (let's say you want to mount multiple independent components to the same div).

Why are you expecting an hydration mismatch? From what I understood you are generating the correct html on the server (p.s. a reproduction is appreciated even if it includes a different programming language)

@pjebs
Copy link
Author

pjebs commented Jan 6, 2025

I'm not generating the correct html (just an approximate) because I haven't quite solved how to generate the EXACT DOM on the server. Even when I replace it with text "SAMPLE BODY", there is still not hydration mismatch error.

@paoloricciuti
Copy link
Member

I'm not generating the correct html (just an approximate) because I haven't quite solved how to generate the EXACT DOM on the server. Even when I replace it with text "SAMPLE BODY", there is still not hydration mismatch error.

Then we absolutely need a reproduction for this, at very minimum you should provide the generated html and how you are actually using it

@paoloricciuti paoloricciuti added the awaiting submitter needs a reproduction, or clarification label Jan 6, 2025
@pjebs
Copy link
Author

pjebs commented Jan 6, 2025

@paoloricciuti I'll create a sample project tomr. Almost midnight here.
What do you think of my proposals above? Do they have merit?

@paoloricciuti
Copy link
Member

@paoloricciuti I'll create a sample project tomr. Almost midnight here. What do you think of my proposals above? Do they have merit?

I think we can clearly state which attributes (even tho you already get the warning)...as per "always preferring client side" I think this is a very niche use and I can't recall on the top of my head the amount of code we would need to unlock that but I suspect is not minimal.

@pjebs
Copy link
Author

pjebs commented Jan 6, 2025

Here is instructions on how to reproduce

  1. npm create vite@latest
  2. Select Svelte -> Typescript
  3. Run npm install

Change index.html to add "SAMPLE BODY":

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Svelte + TS</title>
  </head>
  <body>
    <div id="app">SAMPLE BODY</div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
  1. Update main.ts to switch from mount to hydrate.
  2. Run npm run dev
  3. Open browser and observe console. There is no hydration_mismatch error. The UI updates properly however.

@pjebs
Copy link
Author

pjebs commented Jan 7, 2025

@tanhauhau I know you've worked on the hydration code.
How hard is it set svelte to ALWAYS PREFER CLIENT SIDE RENDERING when there is a hydration_mismatch?

@paoloricciuti
Copy link
Member

Ok i took a look, that is simply because by default hydrate falls back to mount after clearing the content of the div if it immediately encounter an hydration error.

@paoloricciuti paoloricciuti removed the awaiting submitter needs a reproduction, or clarification label Jan 7, 2025
@pjebs
Copy link
Author

pjebs commented Jan 7, 2025

Why am I not notified of the hydration error? Isn't that the point of hydration_mismatch error.

@paoloricciuti
Copy link
Member

Why am I not notified of the hydration error? Isn't that the point of hydration_mismatch error.

The error is there to show you when there's an hydration mismatch but if svelte can "correct that" it falls back to removing the server content and just mounting the component

@pjebs
Copy link
Author

pjebs commented Jan 8, 2025

So essentially it is ALWAYS PREFER CLIENT SIDE RENDERING???
Or if not, what are the conditions in which it prefers client side rendering?
What are the conditions for svelte to be to "correct that".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants